home *** CD-ROM | disk | FTP | other *** search
/ Celestin Apprentice 4 / Apprentice-Release4.iso / Source Code / Pascal / Applications / NIH Image 1.59 / 1.59 Source / Analysis.p < prev    next >
Encoding:
Text File  |  1995-10-24  |  72.5 KB  |  2,475 lines  |  [TEXT/PJMM]

  1. unit Analysis;
  2.  
  3. {Analysis routines used by the NIH Image}
  4.  
  5. interface
  6.  
  7.     uses
  8.         Memory, QuickDraw, Packages, Menus, Events, Fonts, Scrap, ToolUtils, Resources, Errors, StandardFile, Palettes, globals, Utilities, LeastSquares, Graphics, file1, file2, Ellipse, Lut;
  9.  
  10.  
  11.  
  12.     procedure DoHistogram;
  13.     procedure GetRectHistogram;
  14.     procedure GetHistogram;
  15.     procedure ShowContinuousHistogram;
  16.     procedure ComputeResults;
  17.     procedure FindThresholdingMode;
  18.     procedure Measure;
  19.     procedure UpdateRoiLineWidth;
  20.     procedure DoProfilePlotOptions;
  21.     procedure ShowResults;
  22.     procedure PlotDensityProfile;
  23.     procedure SetScale;
  24.     procedure Calibrate;
  25.     procedure ResetCounter;
  26.     procedure DoMeasurementOptions;
  27.     procedure DoPoints (event: EventRecord);
  28.     procedure FindAngle (event: EventRecord);
  29.     procedure SaveBlankField;
  30.     procedure UndoLastMeasurement (DisplayResults: boolean);
  31.     procedure MarkSelection (count: integer);
  32.     procedure AutoOutline (start: point);
  33.     procedure RedoMeasurement;
  34.     procedure DeleteMeasurement;
  35.     procedure AnalyzeParticles;
  36.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  37.     function isBinaryImage: boolean;
  38.     function DoAPDialog: boolean;
  39.  
  40.  
  41. implementation
  42.  
  43.     const
  44.         UnitsPopUpID = 6;
  45.  
  46.     var
  47.         WandMode: (LUTMode, GrayMapMode, BinaryMode);
  48.         GrayMapThreshold: integer;
  49.         InfoForRedirect: InfoPtr;
  50.         UnitsKind: UnitsType;
  51.  
  52.  
  53.  
  54. {$PUSH}
  55. {$D-}
  56.  
  57.  
  58. procedure DoHistogramOfLine (data: ptr; var histogram: HistogramType; width: LongInt);
  59. {$IFC PowerPC}
  60. VAR
  61.   line:LinePtr;
  62.   i,value:integer;
  63. BEGIN
  64.   line:=LinePtr(data);
  65.   FOR i:=0 TO width-1 DO BEGIN
  66.     value:=line^[i];
  67.     histogram[value]:=histogram[value]+1;
  68.   END;
  69. END;
  70. {$ELSEC}
  71.     {a0=data}
  72.     {a1=histogram}
  73.     {d0=width}
  74.     {d1=pixel value}
  75.     inline
  76.         $4E56, $0000, {  link a6,#0}
  77.         $48E7, $C0C0, {  movem.l a0-a1/d0-d1,-(sp)}
  78.         $206E, $000C, {  move.l 12(a6),a0}
  79.         $226E, $0008, {  move.l 8(a6),a1}
  80.         $202E, $0004, {  move.l 4(a6),d0}
  81.         $5380,       {  subq.l #1,d0}
  82.         $4281,       {L clr.l d1}
  83.         $1218,       {  move.b (a0)+,d1}
  84.         $E541,       {  asl.w #2,d1}
  85.         $52B1, $1800, {  addq.l #1,0(a1,d1.l)}
  86.         $51C8, $FFF4, {  dbra d0,L}
  87.         $4CDF, $0303, {  movem.l (sp)+,a0-a1/d0-d1}
  88.         $4E5E,       {  unlk a6}
  89.         $DEFC, $000C; {  add.w #12,sp}
  90. {$ENDC}
  91.  
  92.  
  93.     procedure GetRectHistogram;
  94.         var
  95.             width, i, NumberOfLines: integer;
  96.             offset: LongInt;
  97.             p: ptr;
  98.     begin
  99.         if TooWide then
  100.             exit(GetRectHistogram);
  101.         ShowWatch;
  102.         for i := 0 to 255 do
  103.             Histogram[i] := 0;
  104.         with info^.RoiRect, info^ do begin
  105.                 offset := top * BytesPerRow + left;
  106.                 p := ptr(ord4(PicBaseAddr) + offset);
  107.                 width := right - left;
  108.                 NumberOfLines := bottom - top;
  109.             end;
  110.         if width > 0 then
  111.             for i := 1 to NumberOfLines do begin
  112.                     DoHistogramOfLine(p, histogram, width);
  113.                     p := ptr(ord4(p) + info^.BytesPerRow);
  114.                 end
  115.     end;
  116.  
  117.  
  118.     procedure SetupRedirectedSampling;
  119.         var
  120.             info1, info2, SaveInfo: InfoPtr;
  121.             SameCalibration: boolean;
  122.             i: integer;
  123.     begin
  124.         InfoForRedirect := nil;
  125.         if nPics <> 2 then begin
  126.                 PutError('There must be exactly two image windows open to do redirected sampling.');
  127.                 AnalyzingParticles := false;
  128.                 exit(SetupRedirectedSampling);
  129.             end;
  130.         Info1 := pointer(WindowPeek(PicWindow[1])^.RefCon);
  131.         Info2 := pointer(WindowPeek(PicWindow[2])^.RefCon);
  132.         if not EqualRect(info1^.PicRect, info2^.PicRect) then begin
  133.                 PutError('The image windows must be exactly the same size to do redirected sampling.');
  134.                 AnalyzingParticles := false;
  135.                 exit(SetupRedirectedSampling);
  136.             end;
  137.         if (Info1^.fit <> uncalibrated) or (Info2^.fit <> uncalibrated) then begin
  138.                 SameCalibration := true;
  139.                 if Info1^.fit <> Info2^.fit then
  140.                     SameCalibration := false;
  141.                 if Info1^.nCoefficients <> Info2^.nCoefficients then
  142.                     SameCalibration := false;
  143.                 for i := 1 to info1^.nCoefficients do
  144.                     if Info1^.Coefficient[i] <> Info2^.Coefficient[i] then
  145.                         SameCalibration := false;
  146.                 if not SameCalibration then begin
  147.                         PutError('Both image must be calibrated the same way to do redirected sampling.');
  148.                         AnalyzingParticles := false;
  149.                         exit(SetupRedirectedSampling);
  150.                     end;
  151.             end;
  152.         if info = info1 then
  153.             InfoForRedirect := info2
  154.         else
  155.             InfoForRedirect := info1;
  156.     end;
  157.  
  158.  
  159.     procedure GetHistogram;
  160.         var
  161.             MaskLine, DataLine: LineType;
  162.             width, i, vloc: integer;
  163.             sum, sum2, count, OverFlows: LongInt;
  164.             SaveInfo: InfoPtr;
  165.             value: LongInt;
  166.             trect: rect;
  167.     begin
  168.         if TooWide then
  169.             exit(GetHistogram);
  170.         ShowWatch;
  171.         if RedirectSampling then begin
  172.                 SetupRedirectedSampling;
  173.                 if InfoForRedirect = nil then
  174.                     exit(GetHistogram);
  175.             end
  176.         else
  177.             InfoForRedirect := nil;
  178.         if not SetupMask then
  179.             beep;
  180.         SaveInfo := Info;
  181.         for i := 0 to 255 do
  182.             Histogram[i] := 0;
  183.         if FitEllipse then
  184.             ResetSums;
  185.         trect := info^.RoiRect;
  186.         with trect do begin
  187.                 width := right - left;
  188.                 for vloc := top to bottom - 1 do begin
  189.                         if InfoForRedirect <> nil then
  190.                             Info := InfoForRedirect
  191.                         else
  192.                             Info := SaveInfo;
  193.                         GetLine(left, vloc, width, DataLine);
  194.                         Info := UndoInfo;
  195.                         GetLine(left, vloc, width, MaskLine);
  196.                         if FitEllipse then
  197.                             ComputeSums(vloc - top, width, MaskLine);
  198.                         for i := 0 to width - 1 do
  199.                             if MaskLine[i] = BlackIndex then begin
  200.                                     value := band(DataLine[i],255);
  201.                                     histogram[value] := histogram[value] + 1;
  202.                                 end;
  203.                     end;
  204.             end;
  205.         Info := SaveInfo;
  206.         if not AnalyzingParticles then
  207.             SetupUndo; {Needed for drawing "marching ants".}
  208.     end;
  209.  
  210.  
  211. {$POP}
  212.  
  213.     procedure ComputeResults;
  214.         var
  215.             MaxCount, icount, isum, n: LongInt;
  216.             i: integer;
  217.             sum, sum2, ri, rcount, tSD, rmode, xc, yc: extended;
  218.             Major, Minor, EllipseAngle, hcenter, vcenter, calValue: extended;
  219.             MinCalibratedValue, MaxCalibratedValue, CalibratedMean: extended;
  220.             IgnoreThresholding: boolean;
  221.             ulength, clength: extended;
  222.     begin
  223.         with info^, results do begin
  224.                 case ThresholdingMode of
  225.                     DensitySlice:  begin
  226.                             MinIndex := SliceStart;
  227.                             MaxIndex := SliceEnd;
  228.                         end;
  229.                     GrayMapThresholding:  begin
  230.                             MinIndex := GrayMapThreshold;
  231.                             MaxIndex := 255;
  232.                         end;
  233.                     BinaryImage:  begin
  234.                             MinIndex := BlackIndex;
  235.                             MaxIndex := BlackIndex;
  236.                         end;
  237.                     NoThresholding:  begin
  238.                             MinIndex := 0;
  239.                             MaxIndex := 255;
  240.                         end;
  241.                 end;
  242.                 IgnoreThresholding := RedirectSampling or (IncludeHoles and (AnalyzingParticles or (CurrentTool = Wand)));
  243.                 if IgnoreThresholding then begin
  244.                         MinIndex := 0;
  245.                         MaxIndex := 255;
  246.                     end;
  247.                 while (histogram[MinIndex] = 0) and (MinIndex < 255) do
  248.                     MinIndex := MinIndex + 1;
  249.                 while (histogram[MaxIndex] = 0) and (MaxIndex > 0) do
  250.                     MaxIndex := MaxIndex - 1;
  251.                 MaxCount := 0;
  252.                 sum := 0.0;
  253.                 isum := 0;
  254.                 sum2 := 0.0;
  255.                 n := 0;
  256.                 minCalibratedValue := 10e100;
  257.                 maxCalibratedValue := -10e100;
  258.                 rmode := 0.0;
  259.                 imode := 0;
  260.                 for i := MinIndex to MaxIndex do begin
  261.                         calValue := cvalue[i];
  262.                         icount := histogram[i];
  263.                         rcount := icount;
  264.                         sum := sum + rcount * calValue;
  265.                         isum := isum + icount * i;
  266.                         ri := i;
  267.                         sum2 := sum2 + sqr(calValue) * rcount;
  268.                         n := n + icount;
  269.                         if icount > MaxCount then begin
  270.                                 MaxCount := icount;
  271.                                 rmode := cvalue[i];
  272.                                 imode := i
  273.                             end;
  274.                         if calValue < minCalibratedValue then
  275.                             minCalibratedValue := calValue;
  276.                         if calValue > maxCalibratedValue then
  277.                             maxCalibratedValue := calValue;
  278.                     end;
  279.                 if ContinuousHistoGram then
  280.                     exit(ComputeResults);
  281.                 if n = 0 then begin
  282.                         minCalibratedValue := 0.0;
  283.                         maxCalibratedValue := 0.0;
  284.                     end;
  285.                 if n > 0 then begin
  286.                         CalibratedMean := sum / n;
  287.                         UncalibratedMean := isum / n
  288.                     end
  289.                 else begin
  290.                         CalibratedMean := 0.0;
  291.                         UncalibratedMean := 0.0
  292.                     end;
  293.                 IncrementCounter;
  294.                 mean^[mCount] := CalibratedMean;
  295.                 mMin^[mCount] := minCalibratedValue;
  296.                 mMax^[mCount] := maxCalibratedValue;
  297.                 if mCount <= MaxStandards then
  298.                     umean[mCount] := UncalibratedMean;
  299.                 if n > 0 then begin
  300.                         rcount := n;
  301.                         tSD := (rcount * Sum2 - sqr(sum)) / rcount;
  302.                         if tSD > 0.0 then
  303.                             tSD := sqrt(tSD / (rcount - 1.0))
  304.                         else
  305.                             tSD := 0.0
  306.                     end
  307.                 else
  308.                     tSD := 0.0;
  309.                 sd^[mCount] := tSD;
  310.                 PixelCount^[mCount] := n;
  311.                 ulength := 0.0;
  312.                 clength := 0.0;
  313.                 with RoiRect do
  314.                     case RoiType of
  315.                         RectRoi:  begin
  316.                                 uLength := ((right - left) + (bottom - top)) * 2.0;
  317.                                 cLength := uLength;
  318.                                 if SpatiallyCalibrated then
  319.                                     cLength := ((right - left) / xScale + (bottom - top) / yScale) * 2.0;
  320.                             end;
  321.                         OvalRoi:  begin
  322.                                 uLength := pi * ((right - left) + (bottom - top)) / 2.0;
  323.                                 cLength := uLength;
  324.                                 if SpatiallyCalibrated then
  325.                                     cLength := pi * ((right - left) / xScale + (bottom - top) / yScale) / 2.0;
  326.                             end;
  327.                         LineRoi, SegLineRoi, FreeLineRoi:  begin
  328.                                 GetLengthOrPerimeter(ulength, clength);
  329.                                 nLengths := nLengths + 1;
  330.                             end;
  331.                         PolygonRoi, FreehandRoi, TracedRoi: 
  332.                             if (LengthM in Measurements) or (nLengths > 0) or WandAdjustAreas then
  333.                                 GetLengthOrPerimeter(ulength, clength);
  334.                         otherwise
  335.                     end;
  336.                 if SpatiallyCalibrated then
  337.                     plength^[mCount] := cLength
  338.                 else
  339.                     plength^[mcount] := uLength;
  340.                 if SpatiallyCalibrated then
  341.                     mArea^[mCount] := n / (xScale * yScale)
  342.                 else
  343.                     mArea^[mCount] := n;
  344.                 mode^[mCount] := rmode;
  345.                 if FitEllipse then begin
  346.                     GetEllipseParam(Major, Minor, EllipseAngle, xc, yc);
  347.                     if InvertYCoordinates then
  348.                         yc := PicRect.bottom - yc;
  349.                     if SpatiallyCalibrated then begin
  350.                             Major := Major / xScale;
  351.                             Minor := Minor / xScale;
  352.                             xc := xc / xScale;
  353.                             yc := yc / yScale;
  354.                         end;
  355.                     MajorAxis^[mCount] := Major * 2.0;
  356.                     MinorAxis^[mCount] := Minor * 2.0;
  357.                     orientation^[mCount] := EllipseAngle;
  358.                     xcenter^[mCount] := xc;
  359.                     ycenter^[mCount] := yc;
  360.                 end else begin
  361.                     MajorAxis^[mCount] := 0.0;
  362.                     MinorAxis^[mCount] := 0.0;
  363.                     orientation^[mCount] := 0.0;
  364.                     with RoiRect do begin
  365.                         xc := left + (right - left) / 2.0;
  366.                         yc := top + (bottom - top) / 2.0;
  367.                         if InvertYCoordinates then
  368.                             yc := PicRect.bottom - yc;
  369.                         if SpatiallyCalibrated then begin
  370.                                 xc := xc / xScale;
  371.                                 yc := yc / yScale;
  372.                             end;
  373.                         xcenter^[mCount] := xc;
  374.                         ycenter^[mCount] := yc;
  375.                     end;
  376.                 end;
  377.             end; {with}
  378.         measuring := true;
  379.         InfoMessage := '';
  380.     end;
  381.  
  382.  
  383. {$PUSH}
  384. {$D-}
  385.  
  386.  
  387.     procedure FindThresholdingMode;
  388.     begin
  389.         with info^ do begin
  390.                 if DensitySlicing then
  391.                     ThresholdingMode := DensitySlice
  392.                 else if thresholding then begin
  393.                         ThresholdingMode := GrayMapThresholding;
  394.                         GrayMapThreshold := ColorStart;
  395.                         if GrayMapThreshold < 0 then
  396.                             GrayMapThreshold := 0;
  397.                         if GrayMapThreshold > 255 then
  398.                             GrayMapThreshold := 255;
  399.                     end
  400.                 else if BinaryPic then
  401.                     ThresholdingMode := BinaryImage
  402.                 else
  403.                     ThresholdingMode := NoThresholding;
  404.             end;
  405.     end;
  406.  
  407.  
  408.     procedure Measure;
  409.         var
  410.             AutoSelectAll: boolean;
  411.             SaveN: integer;
  412.     begin
  413.         if NotInBounds then
  414.             exit(Measure);
  415.         with info^ do begin
  416.                 FindThresholdingMode;
  417.                 if ThresholdingMode = BinaryImage then
  418.                     ThresholdingMode := NoThresholding;
  419.                 AutoSelectAll := not RoiShowing;
  420.                 if AutoSelectAll then
  421.                     SelectAll(false);
  422.                 if (RoiType = RectRoi) and (not RedirectSampling) then
  423.                     GetRectHistogram
  424.                 else
  425.                     GetHistogram;
  426.                 if MeasurementToRedo > 0 then begin
  427.                         SaveN := mCount;
  428.                         mCount := MeasurementToRedo - 1;
  429.                         ComputeResults;
  430.                         ShowInfo;
  431.                         mCount := SaveN;
  432.                         MeasurementToRedo := 0;
  433.                         UpdateList;
  434.                     end
  435.                 else begin
  436.                         ComputeResults;
  437.                         ShowInfo;
  438.                         AppendResults;
  439.                         if RoiType = LineRoi then
  440.                             if nLengths = 1 then
  441.                                 if not (LengthM in Measurements) then
  442.                                     UpdateList;
  443.                     end;
  444.                 RoiShowing := true;
  445.                 WhatToUndo := UndoMeasurement;
  446.                 if AutoSelectAll then
  447.                     KillRoi;
  448.                 UpdateScreen(OldRoiRect);
  449.             end;
  450.     end;
  451.  
  452.  
  453.     procedure ShowHistogram;
  454.         var
  455.             htop: integer;
  456.             tport: GrafPtr;
  457.             hrect, prect, srect: rect;
  458.             FirstTime: boolean;
  459.     begin
  460.         GetPort(tPort);
  461.         FirstTime := HistoWindow = nil;
  462.         if FirstTime then begin
  463.                 htop := ScreenHeight - hheight - 10;
  464.                 SetRect(HistoRect, hleft, htop, hleft + hwidth, htop + hheight);
  465.                 HistoWindow := NewWindow(nil, HistoRect, 'Histogram', true, NoGrowDocProc, nil, true, 0);
  466.                 WindowPeek(HistoWindow)^.WindowKind := HistoKind;
  467.             end;
  468.         if FirstTime or (VideoControl = nil) then
  469.             SelectWindow(HistoWindow);
  470.         SetPort(HistoWindow);
  471.         InvalRect(HistoWindow^.PortRect);
  472.         SetPort(tPort);
  473.     end;
  474.  
  475.  
  476.     procedure ShowContinuousHistogram;
  477.         const
  478.             skip = 10;
  479.         var
  480.             i, NumberOfLines: integer;
  481.             offset: LongInt;
  482.             p: ptr;
  483.     begin
  484.         with info^ do
  485.             if (FrameGrabber = QTvdig) and (PictureType = FrameGrabberType) then
  486.                 CopyOffscreen(fgPixMap, osPort^.portPixMap, PicRect, PicRect);
  487.         for i := 0 to 255 do
  488.             Histogram[i] := 0;
  489.         p := ptr(ptr(fgSlotBase));
  490.         NumberOfLines := ((fgHeight) div skip) - 1;
  491.         offset := fgRowBytes * skip;
  492.         for i := 1 to NumberOfLines do begin
  493.                 DoHistogramOfLine(p, histogram, fgWidth);
  494.                 p := ptr(ord4(p) + offset);
  495.             end;
  496.         ThresholdingMode := NoThresholding;
  497.         HistogramSliceStart := 0;
  498.         HistogramSliceEnd := 255;
  499.         ComputeResults;
  500.         ShowHistogram;
  501.     end;
  502.  
  503.  
  504.     procedure DoHistogram;
  505.         var
  506.             AutoSelectAll: boolean;
  507.     begin
  508.         if NotInBounds then
  509.             exit(DoHistogram);
  510.         if digitizing then begin
  511.                 if ContinuousHistogram then
  512.                     ContinuousHistogram := false
  513.                 else begin
  514.                         ContinuousHistogram := true;
  515.                         if info <> NoInfo then
  516.                             with info^ do begin
  517.                                     RoiType := NoRoi;
  518.                                     RoiRect := SrcRect;
  519.                                 end;
  520.                     end;
  521.                 exit(DoHistogram)
  522.             end;
  523.         AutoSelectAll := not info^.RoiShowing;
  524.         if AutoSelectAll then
  525.             SelectAll(false);
  526.         if (info^.RoiType = RectRoi) and (not RedirectSampling) then
  527.             GetRectHistogram
  528.         else
  529.             GetHistogram;
  530.         ThresholdingMode := NoThresholding;
  531.         ComputeResults;
  532.         ShowCount := false;
  533.         ShowInfo;
  534.         ShowCount := true;
  535.         FindThresholdingMode;
  536.         case ThresholdingMode of
  537.             DensitySlice:  begin
  538.                     HistogramSliceStart := SliceStart;
  539.                     HistogramSliceEnd := SliceEnd;
  540.                 end;
  541.             GrayMapThresholding:  begin
  542.                     HistogramSliceStart := GrayMapThreshold;
  543.                     HistogramSliceEnd := 255;
  544.                 end;
  545.             BinaryImage, NoThresholding:  begin
  546.                     HistogramSliceStart := 0;
  547.                     HistogramSliceEnd := 255;
  548.                 end;
  549.         end;
  550.         ShowHistogram;
  551.         UndoLastMeasurement(false);
  552.         WhatToUndo := NothingToUndo;
  553.         if AutoSelectAll then
  554.             KillRoi;
  555.     end;
  556.  
  557.  
  558. {$POP}
  559.  
  560.     procedure PlotDensityProfile;
  561.         var
  562.             hloc, vloc, value, width, height, i: integer;
  563.             aLine: LineType;
  564.             sum: array[0..MaxLine] of real;
  565.             start, p1, p2: point;
  566.     begin
  567.         with info^ do
  568.             if RoiShowing then
  569.                 case RoiType of
  570.                     LineRoi:  begin
  571.                             PlotLineProfile;
  572.                             exit(PlotDensityProfile);
  573.                         end;
  574.                     FreeLineRoi, SegLineRoi, PolygonRoi, FreehandRoi, TracedRoi:  begin
  575.                             PlotArbitraryLine;
  576.                             exit(PlotDensityProfile);
  577.                         end;
  578.                 end; {case}
  579.         if NoSelection or NotRectangular or NotInBounds then
  580.             exit(PlotDensityProfile);
  581.         ShowWatch;
  582.         with info^.RoiRect do begin
  583.                 width := right - left;
  584.                 height := bottom - top;
  585.                 start.h := left;
  586.                 start.v := bottom;
  587.                 if (width >= height) or (OptionKeyWasDown) then begin
  588.             {Column Average Plot}
  589.                         if width > MaxLine then begin
  590.                             PlotTooLongMsg;
  591.                             exit(PlotDensityProfile);
  592.                         end;
  593.                         for i := 0 to width - 1 do
  594.                             sum[i] := 0.0;
  595.                         for vloc := top to bottom - 1 do begin
  596.                                 GetLine(left, vloc, width, aLine);
  597.                                 for i := 0 to width - 1 do
  598.                                     sum[i] := sum[i] + cvalue[aLine[i]];
  599.                             end;
  600.                         for i := 0 to width - 1 do
  601.                             PlotData^[i] := sum[i] / height;
  602.                         PlotCount := width;
  603.                         PlotAvg := height;
  604.                         PlotStart.h := left;
  605.                         PlotStart.v := top + (bottom - top) div 2;
  606.                         PlotAngle := 0.0;
  607.                         ComputePlotMinAndMax;
  608.                         if ShowPlot then
  609.                             SetupPlot(start, false);
  610.                     end
  611.                 else begin
  612.            {Row Average Plot}
  613.                         if height > MaxLine then begin
  614.                             PlotTooLongMsg;
  615.                             exit(PlotDensityProfile);
  616.                         end;
  617.                         for i := 0 to height - 1 do
  618.                             sum[i] := 0.0;
  619.                         for hloc := left to right - 1 do begin
  620.                                 GetColumn(hloc, top, height, aLine);
  621.                                 for i := 0 to height - 1 do
  622.                                     sum[i] := sum[i] + cValue[aLine[i]];
  623.                             end;
  624.                         for i := 0 to height - 1 do
  625.                             PlotData^[i] := sum[i] / width;
  626.                         PlotCount := height;
  627.                         PlotAvg := width;
  628.                         PlotStart.h := left + (right - left) div 2;
  629.                         PlotStart.v := top;
  630.                         PlotAngle := 270.0;
  631.                         ComputePlotMinAndMax;
  632.                         if ShowPlot then
  633.                             SetupPlot(start, true);
  634.                     end;
  635.             end; {with}
  636.     end;
  637.  
  638.  
  639.     procedure SetScaleUProc (d: DialogPtr; item: integer);
  640.      {User proc for Set Scale dialog box}
  641.         var
  642.             str: str255;
  643.             VersInfo: str255;
  644.             r: rect;
  645.     begin
  646.         SetPort(d);
  647.         GetDItemRect(d, item, r);
  648.         DrawDropBox(r);
  649.         GetMenuItemText(UnitsMenuH, ord(UnitsKind) + 1, str);
  650.         DrawPopUpText(str, r);
  651.     end;
  652.  
  653.  
  654.     procedure SetScale;
  655.         const
  656.             MeasuredDistanceID = 3;
  657.             KnownDistanceID = 4;
  658.             AspectRatioID = 5;
  659.             ScaleID = 7;
  660.             UnitsTextID = 8;
  661.         var
  662.             mylog: DialogPtr;
  663.             item, i: integer;
  664.             SaveUnitsKind, OldUnitsKind, MenuUnitsKind: UnitsType;
  665.             KnownDistance, MeasuredDistance, SaveScale, TempScale, CalibratedDistance: extended;
  666.             UnitsPerCM, OldUnitsPerCM, SaveRawScale, SaveAspectRatio: extended;
  667.             ignore, MenuItem: integer;
  668.             str: str255;
  669.             SaveUnits: UnitType;
  670.             isLineSelection: boolean;
  671.             ulength, clength: extended;
  672.             r: rect;
  673.     begin
  674.         if SetScaleUserProc=nil
  675.             then SetScaleUserProc:=NewRoutineDescriptor(@SetScaleUProc, uppUserItemProcInfo, GetCurrentISA);
  676.         with info^ do begin
  677.                 if (not RoiShowing) and (CurrentTool = LineTool) and (NoInfo^.roiType = LineRoi) then
  678.                     RestoreRoi;
  679.                 isLineSelection := RoiShowing and (RoiType = LineRoi);
  680.                 InitCursor;
  681.                 if isLineSelection then begin
  682.                         GetLengthOrPerimeter(ulength, clength);
  683.                         MeasuredDistance := ulength;
  684.                     end
  685.                 else
  686.                     MeasuredDistance := 0.0;
  687.                 if not SpatiallyCalibrated then
  688.                     xUnit := 'pixel';
  689.                 GetUnitsKind(UnitsKind, UnitsPerCM);
  690.                 SaveUnits := xUnit;
  691.                 SaveUnitsKind := UnitsKind;
  692.                 SaveScale := xScale;
  693.                 SaveAspectRatio := PixelAspectRatio;
  694.                 KnownDistance := 0.0;
  695.                 mylog := GetNewDialog(10, nil, pointer(-1));
  696.                 SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  697.                 SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  698.                 SelectdialogItemText(MyLog, KnownDistanceID, 0, 32767);
  699.                 SetDReal(MyLog, AspectRatioID, PixelAspectRatio, 4);
  700.                 SetUProc(myLog, UnitsPopupID, handle(SetScaleUserProc));
  701.                 if UnitsKind = pixels then
  702.                     TempScale := 1.0
  703.                 else
  704.                     TempScale := xScale;
  705.                 if trunc(TempScale) = TempScale then
  706.                     SetDReal(MyLog, ScaleID, TempScale, 0)
  707.                 else
  708.                     SetDReal(MyLog, ScaleID, TempScale, 5);
  709.                 SetDString(MyLog, UnitsTextID, xUnit);
  710.                 setport(myLog);
  711.                 repeat
  712.                     ModalDialog(nil, item);
  713.                     if item = MeasuredDistanceID then
  714.                         MeasuredDistance := GetDReal(MyLog, MeasuredDistanceID);
  715.                     if item = KnownDistanceID then
  716.                         KnownDistance := GetDReal(MyLog, KnownDistanceID);
  717.                     if item = ScaleID then begin
  718.                             MeasuredDistance := GetDReal(MyLog, ScaleID);
  719.                             KnownDistance := 1;
  720.                             SetDReal(MyLog, MeasuredDistanceID, MeasuredDistance, 2);
  721.                             SetDReal(MyLog, KnownDistanceID, KnownDistance, 2);
  722.                         end;
  723.                     if item = AspectRatioID then begin
  724.                             PixelAspectRatio := GetDReal(MyLog, AspectRatioID);
  725.                             if PixelAspectRatio <= 0.0 then begin
  726.                                     beep;
  727.                                     PixelAspectRatio := 1.0;
  728.                                 end;
  729.                         end;
  730.                     if item = UnitsPopUpID then begin
  731.                             OldUnitsKind := UnitsKind;
  732.                             OldUnitsPerCM := UnitsPerCM;
  733.                             GetDItemRect(myLog, item, r);
  734.                             InvertRect(r);
  735.                             MenuItem := PopUpMenu(UnitsMenuH, r.left, r.top, ord(UnitsKind) + 1);
  736.                             InvertRect(r);
  737.                             GetMenuItemText(UnitsMenuH, MenuItem, str);
  738.                             DrawPopUpText(str, r);
  739.                             UnitsKind := UnitsType(MenuItem - 1);
  740.                             GetXUnits(UnitsKind);
  741.                             if (UnitsType(MenuItem - 1) = OtherUnits) and (OldUnitsKind <> OtherUnits) then
  742.                                 xUnit := 'unit';
  743.                             SetDString(MyLog, UnitsTextID, xUnit);
  744.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  745.                             if (UnitsPerCM <> OldUnitsPerCM) and (UnitsPerCM <> 0.0) and (OldUnitsPerCM <> 0.0) then
  746.                                     xScale := xScale * (OldUnitsPerCM / UnitsPerCM);
  747.                             if UnitsKind = Pixels then
  748.                                 KnownDistance := 0.0;
  749.                         end;
  750.                     if (item = KnownDistanceID) or (item = MeasuredDistanceID) or (item = ScaleID) then
  751.                         if (UnitsKind = Pixels) and (item <> cancel) then
  752.                             PutError('Please select a measurent unit (not pixels) before setting or changing the scale.')
  753.                         else begin
  754.                                 if (MeasuredDistance > 0.0) and (KnownDistance > 0.0) then
  755.                                         xScale := MeasuredDistance / KnownDistance;
  756.                             end;
  757.                     if UnitsKind = pixels then
  758.                         TempScale := 1.0
  759.                     else
  760.                         TempScale := xScale;
  761.                     if item <> ScaleID then begin
  762.                             if (trunc(TempScale) = TempScale) or (TempScale >= 10000.0) then
  763.                                 SetDReal(MyLog, ScaleID, TempScale, 0)
  764.                             else if TempScale < 0.01 then
  765.                                 SetDReal(MyLog, ScaleID, TempScale, 6)
  766.                             else
  767.                                 SetDReal(MyLog, ScaleID, TempScale, 3);
  768.                         end;
  769.                     if item = UnitsTextID then begin
  770.                             str := GetDString(myLog, item);
  771.                             TruncateString(str, maxUnit);
  772.                             xUnit := str;
  773.                             GetUnitsKind(UnitsKind, UnitsPerCM);
  774.                             GetDItemRect(myLog, UnitsPopUpID, r);
  775.                             InvalRect(r);
  776.                         end;
  777.                 until (item = ok) or (item = cancel);
  778.                 DisposeDialog(mylog);
  779.                 if item = cancel then begin
  780.                         xUnit := SaveUnits;
  781.                         UnitsKind := SaveUnitsKind;
  782.                         xScale := SaveScale;
  783.                         PixelAspectRatio := SaveAspectRatio;
  784.                     end
  785.                 else
  786.                     Changes := true;
  787.                 SpatiallyCalibrated := (xScale <> 0.0) and (xUnit <> 'pixel');
  788.                 if SpatiallyCalibrated then
  789.                     yScale := xScale / PixelAspectRatio
  790.                 else begin
  791.                     UnitsKind := Pixels;
  792.                     UnitsPerCm := 0.0;
  793.                     PixelAspectRatio:=1.0;
  794.                 end;
  795.                 UpdateTitleBar;
  796.                 if item<>cancel then begin
  797.                     NoInfo^.SpatiallyCalibrated:=SpatiallyCalibrated;
  798.                     NoInfo^.xUnit := xUnit;
  799.                     NoInfo^.xScale := xScale;
  800.                     NoInfo^.PixelAspectRatio := PixelAspectRatio;
  801.                 end;
  802.             end; {with info^}
  803.     end;
  804.  
  805.  
  806. {$PUSH}
  807. {$D-}
  808.  
  809.  
  810.     procedure SetupCalibrationPlot;
  811.         const
  812.             hrange = 1024;
  813.             hmax = 1023;
  814.             vrange = 600;
  815.             vmax = 599;
  816.             SymbolSize = 11;
  817.         var
  818.             fRect, tRect: rect;
  819.             svalue, range, hscale, vscale, MinV, MaxV: extended;
  820.             tPort: GrafPtr;
  821.             i, hloc, vloc: integer;
  822.             SaveClipRegion: RgnHandle;
  823.             pt: point;
  824.     begin
  825.         PlotLeftMargin := 60;
  826.         PlotTopMargin := 15;
  827.         PlotBottomMargin := 30;
  828.         PlotRightMargin := 100;
  829.         MinV := minCValue;
  830.         MaxV := maxCValue;
  831.         for i := 1 to nStandards do begin
  832.                 svalue := StandardValues[i];
  833.                 if svalue < MinV then
  834.                     MinV := svalue;
  835.                 if svalue > MaxV then
  836.                     MaxV := svalue;
  837.             end;
  838.         range := MaxV - MinV;
  839.         PlotWidth := hrange div 3 + PlotLeftMargin + PlotRightMargin;
  840.         PlotHeight := vrange div 3 + PlotTopMargin + PlotBottomMargin;
  841.         PlotLeft := 64;
  842.         PlotTop := 64;
  843.         for i := 0 to 255 do
  844.             PlotData^[i] := cvalue[i];
  845.         PlotAvg := 1;
  846.         PlotCount := 256;
  847.         MakePlotWindow(PlotLeft, PlotTop, PlotWidth, PlotHeight);
  848.         if PlotWindow = nil then
  849.             exit(SetupCalibrationPlot);
  850.         WindowPeek(PlotWindow)^.WindowKind := CalibrationPlotKind;
  851.         SetRect(fRect, -SymbolSize, -SymbolSize, hmax + SymbolSize, vmax + SymbolSize);
  852.         GetPort(tPort);
  853.         SetPort(PlotWindow);
  854.         SaveClipRegion := PlotWindow^.ClipRgn;
  855.         RectRgn(PlotWindow^.ClipRgn, fRect);
  856.         hscale := 256.0 / hrange;
  857.         vscale := range / vrange;
  858.         PlotPICT := OpenPicture(fRect);
  859.         for i := 1 to nStandards do begin
  860.                 hloc := round(umean[i] / hscale);
  861.                 vloc := vmax - round((StandardValues[i] - minCValue) / vscale);
  862.                 SetRect(tRect, hloc - SymbolSize, vloc - SymbolSize, hloc + SymbolSize, vloc + SymbolSize);
  863.                 FrameOval(tRect);
  864.             end;
  865.         MoveTo(0, vmax - round((cvalue[0] - minCValue) / vscale));
  866.         for i := 1 to 255 do begin
  867.                 hloc := round(i / hscale);
  868.                 vloc := vmax - round((cvalue[i] - minCValue) / vscale);
  869.                 LineTo(hloc, vloc);
  870.             end;
  871.         ClosePicture;
  872.         PlotWindow^.ClipRgn := SaveClipRegion;
  873.         InvalRect(PlotWindow^.PortRect);
  874.         SetPort(tPort);
  875.         SelectWindow(PlotWindow);
  876.     end;
  877.  
  878.  
  879.     procedure DoCurveFitting;
  880.         var
  881.             i: integer;
  882.             XData, YData, YFit, Residuals, TempData: ColumnVector;
  883.             Variance: extended;
  884.             SumResidualsSqr, SumStandards, mean, SumMeanDiffSqr, DegreesOfFreedom: extended;
  885.             str1, str2: str255;
  886.     begin
  887.         with info^ do begin
  888.                 ShowWatch;
  889.                 if fit = RodbardFit then { need to reverse x and y to fit Rodbard equation }
  890.                     for i := 1 to nStandards do begin
  891.                             XData[i] := StandardValues[i];
  892.                             YData[i] := umean[i];
  893.                         end
  894.                 else
  895.                     for i := 1 to nStandards do begin
  896.                             XData[i] := umean[i];
  897.                             YData[i] := StandardValues[i];
  898.                         end;
  899.                 case fit of
  900.                     StraightLine: 
  901.                         nCoefficients := 2;
  902.                     Poly2: 
  903.                         nCoefficients := 3;
  904.                     Poly3: 
  905.                         nCoefficients := 4;
  906.                     Poly4: 
  907.                         nCoefficients := 5;
  908.                     Poly5: 
  909.                         nCoefficients := 6;
  910.                     ExpoFit: 
  911.                         nCoefficients := 2;
  912.                     PowerFit: 
  913.                         nCoefficients := 2;
  914.                     LogFit: 
  915.                         nCoefficients := 2;
  916.                     RodbardFit: 
  917.                         nCoefficients := 4;
  918.                 end;
  919.                 DegreesOfFreedom := nStandards - nCoefficients;
  920.                 if DegreesOfFreedom < 0 then begin
  921.                         FitGoodness := 0.0;
  922.                         NumToString(nCoefficients, str1);
  923.                         case fit of
  924.                             StraightLine: 
  925.                                 str2 := 'straight line';
  926.                             Poly2: 
  927.                                 str2 := '2nd degree polynomial';
  928.                             Poly3: 
  929.                                 str2 := '3rd degree polynomial';
  930.                             Poly4: 
  931.                                 str2 := '4th degree polynomial';
  932.                             Poly5: 
  933.                                 str2 := '5th degree polynomial';
  934.                             ExpoFit: 
  935.                                 str2 := 'exponential';
  936.                             PowerFit: 
  937.                                 str2 := 'power';
  938.                             LogFit: 
  939.                                 str2 := 'log';
  940.                             RodbardFit: 
  941.                                 str2 := 'Rodbard';
  942.                         end;
  943.                         str2 := concat(' standards to do ', str2, ' fitting.');
  944.                         PutError(concat('You need at least ', str1, str2));
  945.                         AbortMacro;
  946.                         fit:=Uncalibrated;
  947.                         exit(DoCurveFitting)
  948.                     end;
  949.                 DoSimplexFit(nStandards, nCoefficients, XData, YData, Coefficient, residuals);
  950.                 ZeroClip := true;
  951.                 for i := 1 to nStandards do
  952.                     if ydata[i] < 0.0 then
  953.                         ZeroClip := false;
  954.                 GenerateValues;
  955.                 SumResidualsSqr := 0.0;
  956.                 SumStandards := 0.0;
  957.                 if fit = RodbardFit then
  958.                     for i := 1 to nStandards do begin
  959.                             tempdata[i] := StandardValues[i];
  960.                             StandardValues[i] := umean[i];
  961.                         end;
  962.                 for i := 1 to nStandards do begin
  963.                         SumResidualsSqr := SumResidualsSqr + sqr(residuals[i]);
  964.                         SumStandards := SumStandards + StandardValues[i];
  965.                     end;
  966.                 FitSD := Sqrt(SumResidualsSqr / nStandards);
  967.                 mean := SumStandards / nStandards;
  968.                 SumMeanDiffSqr := 0.0;
  969.                 for i := 1 to nStandards do
  970.                     SumMeanDiffSqr := SumMeanDiffSqr + sqr(StandardValues[i] - Mean);
  971.                 if (SumMeanDiffSqr > 0.0) and (DegreesOfFreedom <> 0) then
  972.                     FitGoodness := 1 - (SumResidualsSqr / DegreesOfFreedom) * ((nStandards - 1) / SumMeanDiffSqr)
  973.                 else
  974.                     FitGoodness := 1.0;
  975.                 if fit = RodbardFit then
  976.                     for i := 1 to nStandards do
  977.                         StandardValues[i] := tempdata[i];
  978.             end;
  979.         info^.changes := true;
  980.     end;
  981.  
  982.  
  983.     procedure GetStandardsFromFile (mylog: DialogPtr; FirstLevelID, FirstStandardID: integer);
  984.         var
  985.             fname, str: str255;
  986.             RefNum, i, nColumns, nValues: integer;
  987.             rLine: RealLine;
  988.     begin
  989.         RefNum := 0;
  990.         if not GetTextFile(fname, RefNum) then
  991.             exit(GetStandardsFromFile);
  992.         InitTextInput(fname, RefNum);
  993. n
  994.                             umean[item - FirstLevelID + 1] := GetDReal(MyLog, item)
  995.                         else begin
  996.                                 PutError('Use the Measure command to record measured values.');
  997.                                 if i <= nStandards then begin
  998.                                         RealToString(umean[i], 1, 2, str);
  999.                                         SetDString(MyLog, item, str)
  1000.                                     end
  1001.                                 else
  1002.                                     SetDString(MyLog, item, '');
  1003.                             end;
  1004.                     end;
  1005.                 if (item >= FirstFitID) and (item <= LastFitID) then begin
  1006.                         for i := FirstFitID to LastFitID do
  1007.                             SetDlogItem(mylog, i, 0);
  1008.                         SetDlogItem(mylog, item, 1);
  1009.                         fit := CurveFitType(item - FirstFitID);
  1010.                     end;
  1011.                 if item = UnitOfMeasureID then begin
  1012.                     str := GetDString(MyLog, item);
  1013.                     TruncateString(str, maxUM);
  1014.                     UnitOfMeasure := str;
  1015.                 end;
  1016.                 if item = OpenID then begin
  1017.                         GetStandardsFromFile(mylog, FirstLevelID, FirstStandardID);
  1018.                         nKnownValues := nStandards;
  1019.                     end;
  1020.                 if (item = SaveID) and (nStandards > 1) then
  1021.                     SaveStandardsToFile(nStandards);
  1022.                 if (item = InvertID) and (nStandards > 1) then
  1023.                     if InvertOD(NewValues) then
  1024.                         for i := 1 to nStandards do begin
  1025.                                 StandardValues[i] := NewValues[i];
  1026.                                 SetDReal(MyLog, FirstStandardID + i - 1, StandardValues[i], 5);
  1027.                             end;
  1028.             until (item = ok) or (item = cancel);
  1029.             DisposeDialog(mylog);
  1030.             DoCalibrateDialog:=item <> cancel;
  1031.         end; {with info^}
  1032.     end; {DoCalibrateDialog}
  1033.  
  1034.  
  1035.     procedure Calibrate;
  1036.         var
  1037.             nBadReals, i: integer;
  1038.             SaveStandards, temp: StandardsArray;
  1039.     begin
  1040.         SaveStandards := StandardValues;
  1041.         if not macro then
  1042.             if not DoCalibrateDialog then begin
  1043.                 StandardValues := SaveStandards;
  1044.                 exit(Calibrate);
  1045.             end;
  1046.         with info^ do begin
  1047.                 if fit = uncalibrated then begin
  1048.                         RemoveDensityCalibration;
  1049.                         exit(calibrate)
  1050.                     end;
  1051.                 nBadReals := 0;
  1052.                 if nStandards > nKnownValues then
  1053.                     nStandards := nKnownValues;
  1054.                 if fit = UncalibratedOD then
  1055.                     SetupUncalibratedOD
  1056.                 else begin
  1057.                         for i := 1 to nStandards do
  1058.                             if StandardValues[i] = BadReal then
  1059.                                 nBadReals := nBadReals + 1;
  1060.                         if (nStandards > 0) and (nBadReals = 0) then
  1061.                             DoCurveFitting
  1062.                         else if fit = uncalibrated then
  1063.                             beep;
  1064.                     end;
  1065.                 if fit <> uncalibrated then begin
  1066.                         if not macro then
  1067.                             SetupCalibrationPlot;
  1068.                     end;
  1069.                 NoInfo^.fit := fit;
  1070.                 NoInfo^.nCoefficients := nCoefficients;
  1071.                 NoInfo^.Coefficient := Coefficient;
  1072.                 NoInfo^.ZeroClip := ZeroClip;
  1073.                 NoInfo^.UnitOfMeasure := UnitOfMeasure;
  1074.                 if (fit<>StraightLine) or (Coefficient[2] <> -1.0) then
  1075.                     InvertPixelValues:=false;
  1076.                 UpdateTitleBar;
  1077.             end; {with info^}
  1078.     end; {Calibrate}
  1079.  
  1080.  
  1081.     procedure ResetCounter;
  1082.         var
  1083.             AlertID: Integer;
  1084.     begin
  1085.         if UnsavedResults and (not macro) then begin
  1086.                 InitCursor;
  1087.                 AlertID := alert(500, nil);
  1088.             end
  1089.         else
  1090.             AlertID := ok;
  1091.         if AlertID <> CancelResetID then begin
  1092.                 nPoints := 0;
  1093.                 nLengths := 0;
  1094.                 nAngles := 0;
  1095.                 mCount := 0;
  1096.                 mCount2 := 0;
  1097.                 UnsavedResults := false;
  1098.                 ShowInfo;
  1099.                 if ResultsWindow <> nil then begin
  1100.                         with ListTE^^ do
  1101.                             TESetSelect(0, teLength, ListTE);
  1102.                         TEDelete(ListTE);
  1103.                         UpdateResultsScrollBars;
  1104.                     end;
  1105.             end;
  1106.         measuring := false;
  1107.     end;
  1108.  
  1109.  
  1110.     procedure ShowResults;
  1111.         const
  1112.             FontSize = 9;
  1113.         var
  1114.             wrect, crect, trect: rect;
  1115.             loc: point;
  1116.     begin
  1117.         mCount2 := mCount;
  1118.         if ResultsWindow <> nil then begin
  1119.                 SelectWindow(ResultsWindow);
  1120.                 exit(ShowResults);
  1121.             end;
  1122.         CopyResultsToBuffer(1, mCount, true);
  1123.         ShowMessage('');
  1124.         ResultsWidth := 110 + round(nListColumns * FieldWidth * 6.5);
  1125.         if ResultsWidth < 250 then
  1126.             ResultsWidth := 250;
  1127.         if (ResultsWidth + 20) > ScreenWidth then
  1128.             ResultsWidth := ScreenWidth - 20;
  1129.         ResultsHeight := ((TextBufLineCount * 2) + 2) * FontSize;
  1130.         if ResultsHeight < 200 then
  1131.             ResultsHeight := 200;
  1132.         if (ResultsHeight + ResultsTop + 50) > ScreenHeight then
  1133.             ResultsHeight := ScreenHeight - ResultsTop - 50;
  1134.         SetRect(wrect, ResultsLeft, ResultsTop, ResultsLeft + ResultsWidth, ResultsTop + ResultsHeight);
  1135.         ResultsWindow := NewWindow(nil, wrect, 'Results', true, 0, pointer(-1), true, 0);
  1136.         WindowPeek(ResultsWindow)^.WindowKind := ResultsKind;
  1137.         SetRect(crect, ResultsWidth - ScrollBarWidth, -1, ResultsWidth + 1, ResultsHeight - 14);
  1138.         vScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsHeight - 14, ScrollBarProc, 0);
  1139.         SetRect(crect, -1, ResultsHeight - ScrollBarWidth, ResultsWidth - 14, ResultsHeight + 1);
  1140.         hScrollBar := NewControl(ResultsWindow, crect, '', true, 0, 0, ResultsWidth - 14, ScrollBarProc, 0);
  1141.         InitResultsTextEdit(Monaco, FontSize);
  1142.         DrawControls(ResultsWindow);
  1143.         WhatToUndo := NothingToUndo;
  1144.     end;
  1145.  
  1146.  
  1147.     procedure DoMeasurementOptions;
  1148.         const
  1149.             FirstID = 3;
  1150.             LastID = 15;
  1151.             RedirectID = 22;
  1152.             IncludeHolesID = 23;
  1153.             AutoID = 24;
  1154.             AdjustID = 25;
  1155.             HeadingsID = 26;
  1156.             MaxMeasurementsID = 21;
  1157.             WidthID = 19;
  1158.             PrecisionID = 17;
  1159.         var
  1160.             mylog: DialogPtr;
  1161.             item, i, SavePrecision, SaveMaxMeasurements, SaveWidth: integer;
  1162.             mtype: MeasurementTypes;
  1163.             SaveMeasurements: SetOfMeasurements;
  1164.             SaveRedirect: boolean;
  1165.             SaveAuto, SaveAdjust, SaveHeadings: boolean;
  1166.     begin
  1167.         InitCursor;
  1168.         if nPoints > 0 then
  1169.             Measurements := Measurements + [XYLocM];
  1170.         if nLengths > 0 then
  1171.             Measurements := Measurements + [LengthM];
  1172.         if nAngles > 0 then
  1173.             Measurements := Measurements + [AngleM];
  1174.         SaveMeasurements := measurements;
  1175.         SaveRedirect := RedirectSampling;
  1176.         SaveWidth := FieldWidth;
  1177.         SavePrecision := precision;
  1178.         SaveAuto := WandAutoMeasure;
  1179.         SaveAdjust := WandAdjustAreas;
  1180.         SaveMaxMeasurements := MaxMeasurements;
  1181.         SaveHeadings := ShowHeadings;
  1182.         mylog := GetNewDialog(4000, nil, pointer(-1));
  1183.         mtype := AreaM;
  1184.         for i := FirstID to LastID do begin
  1185.                 if mtype in measurements then
  1186.                     SetDlogItem(mylog, i, 1);
  1187.                 if i <> LastID then
  1188.                     mtype := succ(mtype);
  1189.             end;
  1190.         SetDlogItem(mylog, RedirectID, ord(RedirectSampling));
  1191.         SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1192.         SetDlogItem(mylog, AutoID, ord(WandAutoMeasure));
  1193.         SetDlogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1194.         SetDlogItem(mylog, HeadingsID, ord(ShowHeadings));
  1195.         SetDNum(MyLog, MaxMeasurementsID, MaxMeasurements);
  1196.         SetDNum(MyLog, WidthID, FieldWidth);
  1197.         SetDNum(MyLog, PrecisionID, precision);
  1198.         repeat
  1199.             ModalDialog(nil, item);
  1200.             if (item >= FirstID) and (item <= LastID) then begin
  1201.                     i := item - FirstID;
  1202.                     case i of
  1203.                         0: 
  1204.                             mtype := AreaM;
  1205.                         1: 
  1206.                             mtype := MeanM;
  1207.                         2: 
  1208.                             mtype := StdDevM;
  1209.                         3: 
  1210.                             mtype := xyLocM;
  1211.                         4: 
  1212.                             mtype := ModeM;
  1213.                         5: 
  1214.                             mtype := LengthM;
  1215.                         6: 
  1216.                             mtype := MajorAxisM;
  1217.                         7: 
  1218.                             mtype := MinorAxisM;
  1219.                         8: 
  1220.                             mtype := AngleM;
  1221.                         9: 
  1222.                             mtype := IntDenM;
  1223.                         10: 
  1224.                             mtype := MinMaxM;
  1225.                         11: 
  1226.                             mtype := User1M;
  1227.                         12: 
  1228.                             mtype := User2M;
  1229.                     end;
  1230.                     if mtype in measurements then begin
  1231.                             measurements := measurements - [mtype];
  1232.                             SetDlogItem(mylog, item, 0)
  1233.                         end
  1234.                     else begin
  1235.                             measurements := measurements + [mtype];
  1236.                             SetDlogItem(mylog, item, 1)
  1237.                         end;
  1238.                 end;
  1239.             if item = RedirectID then begin
  1240.                     RedirectSampling := not RedirectSampling;
  1241.                     SetDlogItem(mylog, RedirectID, ord(RedirectSampling));
  1242.                 end;
  1243.             if item = IncludeHolesID then begin
  1244.                     IncludeHoles := not IncludeHoles;
  1245.                     SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  1246.                 end;
  1247.             if item = AutoID then begin
  1248.                     WandAutoMeasure := not WandAutoMeasure;
  1249.                     SetDlogItem(mylog, AutoID, ord(WandAutoMeasure));
  1250.                 end;
  1251.             if item = AdjustID then begin
  1252.                     WandAdjustAreas := not WandAdjustAreas;
  1253.                     SetDlogItem(mylog, AdjustID, ord(WandAdjustAreas));
  1254.                 end;
  1255.             if item = HeadingsID then begin
  1256.                     ShowHeadings := not ShowHeadings;
  1257.                     SetDlogItem(mylog, HeadingsID, ord(ShowHeadings));
  1258.                 end;
  1259.             if item = WidthID then
  1260.                 FieldWidth := GetDNum(MyLog, WidthID);
  1261.             if item = PrecisionID then
  1262.                 precision := GetDNum(MyLog, PrecisionID);
  1263.             if item = MaxMeasurementsID then
  1264.                 MaxMeasurements := GetDNum(MyLog, MaxMeasurementsID);
  1265.         until (item = ok) or (item = cancel);
  1266.         DisposeDialog(mylog);
  1267.         if (FieldWidth < 1) or (FieldWidth > 18) then begin
  1268.                 FieldWidth := SaveWidth;
  1269.                 beep;
  1270.             end;
  1271.         if (precision < 0) or (precision > 8) then begin
  1272.                 precision := SavePrecision;
  1273.                 beep;
  1274.             end;
  1275.         if (MaxMeasurements < 1) or (MaxMeasurements > MaxMaxRegions) then begin
  1276.                 MaxMeasurements := SaveMaxMeasurements;
  1277.                 beep;
  1278.             end;
  1279.         if item = cancel then begin
  1280.                 measurements := SaveMeasurements;
  1281.                 RedirectSampling := SaveRedirect;
  1282.                 FieldWidth := SaveWidth;
  1283.                 precision := SavePrecision;
  1284.                 WandAutoMeasure := SaveAuto;
  1285.                 WandAdjustAreas := SaveAdjust;
  1286.                 MaxMeasurements := SaveMaxMeasurements;
  1287.                 ShowHeadings := SaveHeadings;
  1288.             end;
  1289.         if not (XYLocM in Measurements) then
  1290.             nPoints := 0;
  1291.         if not (LengthM in Measurements) then
  1292.             nLengths := 0;
  1293.         if not (AngleM in Measurements) then
  1294.             nAngles := 0;
  1295.         UpdateFitEllipse;
  1296.         if MaxMeasurements <> SaveMaxMeasurements then begin
  1297.                 PutError('You must quit and restart NIH Image before the change to Max Measurements will take effect.');
  1298.                 SaveSettings;
  1299.                 MaxMeasurements:=SaveMaxMeasurements;
  1300.             end;
  1301.         if (Measurements <> SaveMeasurements) or (SaveWidth <> FieldWidth) or (SavePrecision <> Precision) then
  1302.             UpdateList;
  1303.     end;
  1304.  
  1305.  
  1306.     procedure UpdateRoiLineWidth;
  1307.     begin
  1308.         with info^, info^.RoiRect do
  1309.             if RoiShowing and (RoiType = LineRoi) then begin
  1310.                     LX1 := left + LX1;
  1311.                     LY1 := top + LY1;
  1312.                     LX2 := left + LX2;
  1313.                     LY2 := top + LY2;
  1314.                     MakeRegion;
  1315.                 end;
  1316.     end;
  1317.  
  1318.  
  1319.     procedure DoProfilePlotOptions;
  1320.         const
  1321.             FixedScaleID = 7;
  1322.             MinID = 8;
  1323.             MaxID = 9;
  1324.             FixedSizeID = 10;
  1325.             WidthID = 11;
  1326.             HeightID = 12;
  1327.             LineWidthID = 13;
  1328.             LinePlotID = 14;
  1329.             ScatterPlotID = 15;
  1330.             InvertID = 16;
  1331.             LabelsID = 17;
  1332.         var
  1333.             mylog: DialogPtr;
  1334.             item, i: integer;
  1335.             SaveAutoscale, SaveLinePlot, SaveInvert, SaveDrawLabels, SaveFixedSize: boolean;
  1336.             SaveWidth, SaveHeight, SaveLineWidth, SaveLineIndex: integer;
  1337.             SaveMin, SaveMax: extended;
  1338.     begin
  1339.         InitCursor;
  1340.         SaveAutoscale := AutoscalePlots;
  1341.         SaveLinePlot := LinePlot;
  1342.         SaveInvert := InvertPlots;
  1343.         SaveMin := ProfilePlotMin;
  1344.         SaveMax := ProfilePlotMax;
  1345.         SaveLineWidth := LineWidth;
  1346.         SaveLineIndex := LineIndex;
  1347.         SaveWidth := ProfilePlotWidth;
  1348.         SaveHeight := ProfilePlotHeight;
  1349.         SaveDrawLabels := DrawPlotLabels;
  1350.         mylog := GetNewDialog(5000, nil, pointer(-1));
  1351.         SetDlogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1352.         SetDReal(MyLog, MinID, ProfilePlotMin, 2);
  1353.         SetDReal(MyLog, MaxID, ProfilePlotMax, 2);
  1354.         SetDlogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1355.         SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1356.         SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1357.         if LinePlot then
  1358.             SetDlogItem(mylog, LinePlotID, 1)
  1359.         else
  1360.             SetDlogItem(mylog, ScatterPlotID, 1);
  1361.         if InvertPlots then
  1362.             SetDlogItem(mylog, InvertID, 1);
  1363.         if DrawPlotLabels then
  1364.             SetDlogItem(mylog, LabelsID, 1);
  1365.         SetDNum(MyLog, LineWidthID, LineWidth);
  1366.         repeat
  1367.             ModalDialog(nil, item);
  1368.             if item = FixedScaleID then begin
  1369.                     AutoscalePlots := not AutoscalePlots;
  1370.                     SetDlogItem(mylog, FixedScaleID, ord(not AutoscalePlots));
  1371.                 end;
  1372.             if item = MinID then begin
  1373.                     ProfilePlotMin := GetDReal(MyLog, MinID);
  1374.                     AutoscalePlots := false;
  1375.                     SetDlogItem(mylog, FixedScaleID, 1);
  1376.                 end;
  1377.             if item = MaxID then begin
  1378.                     ProfilePlotMax := GetDReal(MyLog, MaxID);
  1379.                     AutoscalePlots := false;
  1380.                     SetDlogItem(mylog, FixedScaleID, 1);
  1381.                 end;
  1382.             if item = FixedSizeID then begin
  1383.                     FixedSizePlot := not FixedSizePlot;
  1384.                     SetDlogItem(mylog, FixedSizeID, ord(FixedSizePlot));
  1385.                 end;
  1386.             if item = WidthID then begin
  1387.                     ProfilePlotWidth := GetDNum(MyLog, WidthID);
  1388.                     if (ProfilePlotWidth < 0) or (ProfilePlotWidth > 1023) then begin
  1389.                             ProfilePlotWidth := SaveWidth;
  1390.                             SetDNum(MyLog, WidthID, ProfilePlotWidth);
  1391.                         end;
  1392.                     FixedSizePlot := true;
  1393.                     SetDlogItem(mylog, FixedSizeID, 1);
  1394.                 end;
  1395.             if item = HeightID then begin
  1396.                     ProfilePlotHeight := GetDNum(MyLog, HeightID);
  1397.                     if (ProfilePlotHeight < 0) or (ProfilePlotHeight > 1023) then begin
  1398.                             ProfilePlotHeight := SaveHeight;
  1399.                             SetDNum(MyLog, HeightID, ProfilePlotHeight);
  1400.                         end;
  1401.                     FixedSizePlot := true;
  1402.                     SetDlogItem(mylog, FixedSizeID, 1);
  1403.                 end;
  1404.             if (item = LinePlotID) or (item = ScatterPlotID) then begin
  1405.                     SetDlogItem(mylog, LinePlotID, 0);
  1406.                     SetDlogItem(mylog, ScatterPlotID, 0);
  1407.                     SetDlogItem(mylog, item, 1);
  1408.                     LinePlot := item = LinePlotID;
  1409.                 end;
  1410.             if item = InvertID then begin
  1411.                     InvertPlots := not InvertPlots;
  1412.                     SetDlogItem(mylog, InvertID, ord(InvertPlots));
  1413.                 end;
  1414.             if item = LabelsID then begin
  1415.                     DrawPlotLabels := not DrawPlotLabels;
  1416.                     if DrawPlotLabels then {Attempt to fix a "sticky" check box bug.}
  1417.                         SetDlogItem(mylog, LabelsID, 1)
  1418.                     else
  1419.                         SetDlogItem(mylog, LabelsID, 0);
  1420.                 end;
  1421.             if item = LineWidthID then begin
  1422.                     LineWidth := GetDNum(MyLog, LineWidthID);
  1423.                     if (LineWidth < 1) or (LineWidth > 500) then begin
  1424.                             LineWidth := SaveLineWidth;
  1425.                             SetDNum(MyLog, LineWidthID, LineWidth);
  1426.                         end;
  1427.                     ShowLineWidth;
  1428.                 end;
  1429.         until (item = ok) or (item = cancel);
  1430.         DisposeDialog(mylog);
  1431.         if item = cancel then begin
  1432.                 ProfilePlotWidth := SaveWidth;
  1433.                 ProfilePlotHeight := SaveHeight;
  1434.                 AutoscalePlots := SaveAutoscale;
  1435.                 LinePlot := SaveLinePlot;
  1436.                 InvertPlots := SaveInvert;
  1437.                 ProfilePlotMin := SaveMin;
  1438.                 ProfilePlotMax := SaveMax;
  1439.                 DrawPlotLabels := SaveDrawLabels;
  1440.                 LineWidth := SaveLineWidth;
  1441.                 if LineIndex <> SaveLineIndex then begin
  1442.                         LineIndex := SaveLineIndex;
  1443.                         DrawTools;
  1444.                     end;
  1445.             end;
  1446.         if LineWidth <> SaveLineWidth then
  1447.             UpdateRoiLineWidth;
  1448.         if ProfilePlotMax <= ProfilePlotMin then begin
  1449.                 ProfilePlotMin := SaveMin;
  1450.                 ProfilePlotMax := SaveMax;
  1451.             end;
  1452.     end;
  1453.  
  1454.  
  1455.     procedure DoPoints (event: EventRecord);
  1456.         var
  1457.             loc, tloc: point;
  1458.             hloc, vloc, y, offset: LongInt;
  1459.             r: rect;
  1460.             str, str1, str2: str255;
  1461.             Decrement: boolean;
  1462.             SaveGDevice: GDHandle;
  1463.     begin
  1464.         Decrement := false;
  1465.         SaveGDevice := GetGDevice;
  1466.         SetGDevice(osGDevice);
  1467.         SetPort(GrafPtr(info^.osPort));
  1468.         pmForeColor(ForegroundIndex);
  1469.         loc := event.where;
  1470.         ScreenToOffscreen(loc);
  1471.         with loc do begin
  1472.                 hloc := h;
  1473.                 vloc := v;
  1474.             end;
  1475.         with results, Info^ do begin
  1476.                 nPoints := nPoints + 1;
  1477.                 IncrementCounter;
  1478.                 if InvertYCoordinates then
  1479.                     y := info^.PicRect.bottom - vloc - 1
  1480.                 else
  1481.                     y := vloc;
  1482.                 ClearResults(mCount);
  1483.                 PixelCount^[mCount] := 1;
  1484.                 if SpatiallyCalibrated then
  1485.                     mArea^[mCount] := 1.0 / xScale * yScale
  1486.                 else
  1487.                     mArea^[mCount] := 1;
  1488.                 mean^[mCount] := cvalue[MyGetPixel(hloc, vloc)];
  1489.                 with info^ do
  1490.                     if SpatiallyCalibrated then begin
  1491.                             xcenter^[mCount] := hloc / xScale;
  1492.                             ycenter^[mCount] := y / yScale;
  1493.                         end
  1494.                     else begin
  1495.                             xcenter^[mCount] := hloc;
  1496.                             ycenter^[mCount] := y;
  1497.                         end;
  1498.             end;
  1499.         PenNormal;
  1500.         if OptionKeyDown then begin
  1501.                 NumToString(mCount, str);
  1502.                 tloc := loc;
  1503.                 tloc.v := tloc.v + CurrentSize div 3;
  1504.                 DrawTextString(str, tloc, TeJustCenter);
  1505.             end
  1506.         else begin
  1507.                 offset := LineWidth div 2;
  1508.                 SetRect(r, hloc - offset, vloc - offset, hloc + offset + 1, vloc + offset + 1);
  1509.                 if ShiftKeyDown then begin
  1510.                         Decrement := true;
  1511.                         EraseOval(r);
  1512.                         mcount := mcount - 2;
  1513.                         if mcount <= 0 then begin
  1514.                                 mcount := 0;
  1515.                                 UnsavedResults := false;
  1516.                             end;
  1517.                         nPoints := nPoints - 2;
  1518.                         if nPoints < 0 then
  1519.                             nPoints := 0;
  1520.                     end
  1521.                 else
  1522.                     PaintOval(r);
  1523.                 UpdateScreen(r);
  1524.                 if ControlKeyDown then
  1525.                     with info^ do begin
  1526.                             if SpatiallyCalibrated then begin
  1527.                                     RealToString(hloc / xScale, 1, Precision, str1);
  1528.                                     RealToString(y / yScale, 1, Precision, str2);
  1529.                                 end
  1530.                             else begin
  1531.                                     NumToString(hloc, str1);
  1532.                                     NumToString(y, str2);
  1533.                                 end;
  1534.                             tloc := loc;
  1535.                             with tloc do begin
  1536.                                     h := h + offset + 5;
  1537.                                     v := v + CurrentSize div 3;
  1538.                                 end;
  1539.                             str := concat('(', str1, ', ', str2, ')');
  1540.                             DrawTextString(str, tloc, TeJustLeft);
  1541.                         end; {Control Key Down}
  1542.             end;
  1543.         SetGDevice(SaveGDevice);
  1544.         InfoMessage := '';
  1545.         ShowInfo;
  1546.         if Decrement then begin
  1547.                 DeleteLines(mcount + 1, mcount + 1);
  1548.                 WhatToUndo := NothingToUndo;
  1549.             end
  1550.         else begin
  1551.                 AppendResults;
  1552.                 if (nPoints = 1) then
  1553.                     if not (XYlocM in Measurements) then
  1554.                         UpdateList;
  1555.                 measuring := true;
  1556.                 WhatToUndo := UndoPoint;
  1557.             end;
  1558.     end;
  1559.  
  1560.  
  1561.     procedure FindAngle (event: EventRecord);
  1562.         var
  1563.             start, finish, OldFinish, MidPoint, first: point;
  1564.             ticks: LongInt;
  1565.             x1, y1, x2, y2: integer;
  1566.             angle, angle1, angle2: extended;
  1567.             StartRect: rect;
  1568.             FirstLineDone: boolean;
  1569.     begin
  1570.         if NoUndo then
  1571.             exit(FindAngle);
  1572.         DrawLabels('Angle:', '', '');
  1573.         FlushEvents(EveryEvent, 0);
  1574.         start := event.where;
  1575.         Pt2Rect(start, start, StartRect);
  1576.         InsetRect(StartRect, -2, -2);
  1577.         finish := start;
  1578.         SetPort(info^.wptr);
  1579.         PenNormal;
  1580.         PenMode(PatXor);
  1581.         PenSize(1, 1);
  1582.         MoveTo(start.h, start.v);
  1583.         first := start;
  1584.         repeat
  1585.             repeat
  1586.                 OldFinish := finish;
  1587.                 GetMouse(finish);
  1588.                 MoveTo(start.h, start.v);
  1589.                 LineTo(OldFinish.h, OldFinish.v);
  1590.                 MoveTo(start.h, start.v);
  1591.                 LineTo(finish.h, finish.v);
  1592.                 ticks := TickCount;
  1593.                 while ticks = TickCount do
  1594.                     ;
  1595.                 x1 := finish.h - start.h;
  1596.                 y1 := start.v - finish.v;
  1597.                 angle1 := GetAngle(x1, info^.PixelAspectRatio * y1);
  1598.                 Show1Value(angle1, NoValue);
  1599.             until GetNextEvent(mUpMask, event);
  1600.             FirstLineDone := not PtInRect(finish, StartRect);
  1601.             if not FirstLineDone then
  1602.                 start := finish;
  1603.         until FirstLineDone;
  1604.         MidPoint := finish;
  1605.         x1 := start.h - MidPoint.h;
  1606.         y1 := MidPoint.v - start.v;
  1607.         angle1 := GetAngle(x1, info^.PixelAspectRatio * y1);
  1608.         start := finish;
  1609.         finish := start;
  1610.         repeat
  1611.             OldFinish := finish;
  1612.             GetMouse(finish);
  1613.             MoveTo(start.h, start.v);
  1614.             LineTo(OldFinish.h, OldFinish.v);
  1615.             MoveTo(start.h, start.v);
  1616.             LineTo(finish.h, finish.v);
  1617.             ticks := TickCount;
  1618.             while ticks = TickCount do
  1619.                 ;
  1620.             x2 := finish.h - MidPoint.h;
  1621.             y2 := MidPoint.v - finish.v;
  1622.             angle2 := GetAngle(x2, info^.PixelAspectRatio * y2);
  1623.             with results do begin
  1624.                     if angle1 >= angle2 then
  1625.                         angle := angle1 - angle2
  1626.                     else
  1627.                         angle := angle2 - angle1;
  1628.                     if angle > 180.0 then
  1629.                         angle := 360.0 - angle;
  1630.                     Show1Value(angle, NoValue);
  1631.                 end;
  1632.         until GetNextEvent(mUpMask, event);
  1633.         nAngles := nAngles + 1;
  1634.         IncrementCounter;
  1635.         ClearResults(mCount);
  1636.         Orientation^[mCount] := angle;
  1637.         InfoMessage := '';
  1638.         ShowInfo;
  1639.         AppendResults;
  1640.         if nAngles = 1 then
  1641.             UpdateList;
  1642.         repeat
  1643.         until not GetNextEvent(EveryEvent, Event); {FlushEvent doesn't work under A/UX!}
  1644.         xCoordinates^[1] := first.h;
  1645.         yCoordinates^[1] := first.v;
  1646.         xCoordinates^[2] := midpoint.h;
  1647.         yCoordinates^[2] := midpoint.v;
  1648.         xCoordinates^[3] := finish.h;
  1649.         yCoordinates^[3] := finish.v;
  1650.         nCoordinates := 3;
  1651.         MakeNonStraightLineRoi(SegLineRoi);
  1652.     end;
  1653.  
  1654.  
  1655.     procedure SaveBlankField;
  1656.         var
  1657.             SaveInfo: InfoPtr;
  1658.             i, xLines, xPixelsPerLine: integer;
  1659.             src, dst: ptr;
  1660.             SaveFlag: boolean;
  1661.             name: str255;
  1662.     begin
  1663.         if info^.PictureType = FrameGrabberType then begin
  1664.                 GetWTitle(info^.wptr, name);
  1665.                 if pos('(Corrected)', name) > 0 then begin
  1666.                         PutError('To save a blank field the captured image must be uncorrected.');
  1667.                         exit(SaveBlankField);
  1668.                     end;
  1669.                 SaveInfo := info;
  1670.                 if BlankFieldInfo = nil then begin
  1671.                         if not Duplicate('Blank Field', true) then
  1672.                             exit(SaveBlankField);
  1673.                     end;
  1674.                 src := info^.PicBaseAddr;
  1675.                 dst := BlankFieldInfo^.PicBaseAddr;
  1676.                 with Info^.PicRect do begin
  1677.                         xLines := bottom - top;
  1678.                         xPixelsPerLine := right - left;
  1679.                     end;
  1680.                 for i := 1 to xLines do begin
  1681.                         BlockMove(src, dst, xPixelsPerLine);
  1682.                         src := ptr(ord4(src) + info^.BytesPerRow);
  1683.                         dst := ptr(ord4(dst) + xPixelsPerLine);
  1684.                     end;
  1685.                 Info := BlankFieldInfo;
  1686.                 InvertPic;
  1687.                 SaveFlag := digitizing;
  1688.                 digitizing := false;
  1689.                 SelectAll(false);
  1690.                 ShowCount := false;
  1691.                 Measure;
  1692.                 ShowCount := true;
  1693.                 digitizing := SaveFlag;
  1694.                 BlankFieldMean := round(results.UncalibratedMean);
  1695.                 UndoLastMeasurement(false);
  1696.                 KillRoi;
  1697.                 UpdatePicWindow;
  1698.                 info := SaveInfo;
  1699.                 SelectWindow(Info^.wptr);
  1700.             end;
  1701.     end;
  1702.  
  1703.  
  1704.     procedure UndoLastMeasurement (DisplayResults: boolean);
  1705.     begin
  1706.         if mCount > 0 then begin
  1707.                 if DisplayResults then
  1708.                     DeleteLines(mCount, mCount);
  1709.                 mCount := mCount - 1;
  1710.                 if mCount = 0 then
  1711.                     UnsavedResults := false;
  1712.             end
  1713.         else
  1714.             WhatToUndo := NothingToUndo;
  1715.         if DisplayResults then
  1716.             ShowInfo;
  1717.     end;
  1718.  
  1719.  
  1720.     function PixelInside (hloc, vloc: integer): boolean;
  1721.         var
  1722.             value: integer;
  1723.     begin
  1724.         value := MyGetPixel(hloc, vloc);
  1725.         case ThresholdingMode of
  1726.             DensitySlice: 
  1727.                 PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  1728.             GrayMapThresholding: 
  1729.                 PixelInside := value >= GrayMapThreshold;
  1730.             BinaryImage: 
  1731.                 PixelInside := value = BlackIndex;
  1732.         end;
  1733.     end;
  1734.  
  1735.  
  1736.     function TraceEdge (hstart, vstart: integer; StartingDirection: char; var TouchingEdge: boolean): boolean;
  1737.  
  1738.    {Traces the points(not pixels) that define the edge of an object using the following}
  1739.    {16 entry lookup table and converts the resulting outline to a QuickDraw region.}
  1740.  
  1741.       {Index  1234*  Code    Result}
  1742.  
  1743.       {0         0000     X      Should never happen}
  1744.       {1         000X     R      Go Right}
  1745.       {2         00X0     D     Go Down}
  1746.       {3         00XX     R     Go Right}
  1747.       {4         0X00     U     Go Up}
  1748.       {5         0X0X     U     Go Up}
  1749.       {6         0XX0     u      Go up or down depending on current direction}
  1750.       {7         0XXX     U      Go up}
  1751.       {8         X000     L      Go left}
  1752.       {9         X00X     l       Go left or right depending on current direction}
  1753.       {10        X0X0     D      Go down}
  1754.       {11        X0XX     R      Go right}
  1755.       {12        XX00     L      Go left}
  1756.       {13        XX0X     L      Go left}
  1757.       {14        XXX0     D     Go down}
  1758.       {15        XXXX     X     Should never happen}
  1759.  
  1760.        {*   1=Upper left pixel,  2=Upper right pixel, 3=Lower left pixel, 4=Lower right pixel}
  1761.  
  1762.         var
  1763.             count, hloc, vloc, index, SaveBackground: integer;
  1764.             Saveport: GrafPtr;
  1765.             direction, NewDirection: char;
  1766.             table: string[16];
  1767.             UL, UR, LL, LR, SaveCoordinates: boolean;
  1768.             TempRgn: RgnHandle;
  1769.     begin
  1770.         TouchingEdge := false;
  1771.         table := 'XRDRUUuULlDRLLDX';
  1772.         GetPort(SavePort);
  1773.         SetPort(GrafPtr(info^.osPort));
  1774.         if SelectionMode <> NewSelection then
  1775.             TempRgn := NewRgn;
  1776.         with info^ do begin
  1777.                 SaveBackground := BackgroundIndex; {We want MyGetPixel to always return 0}
  1778.                 BackgroundIndex := WhiteIndex;         {for coordinates beyond the edge of the image.}
  1779.                 PenNormal;
  1780.                 OpenRgn;
  1781.                 direction := StartingDirection;
  1782.                 hloc := hstart;
  1783.                 vloc := vstart;
  1784.                 UL := PixelInside(hloc - 1, vloc - 1);
  1785.                 UR := PixelInside(hloc, vloc - 1);
  1786.                 LL := PixelInside(hloc - 1, vloc);
  1787.                 LR := PixelInside(hloc, vloc);
  1788.                 MoveTo(hstart, vstart);
  1789.                 SaveCoordinates := not MakingLOI;
  1790.                 if SaveCoordinates then begin
  1791.                         xCoordinates^[1] := hstart;
  1792.                         yCoordinates^[1] := vstart;
  1793.                     end;
  1794.                 count := 1;
  1795.                 repeat
  1796.                     if IgnoreParticlesTouchingEdge then
  1797.                         with info^.PicRect do
  1798.                             TouchingEdge := TouchingEdge or (hloc = left) or (hloc = right) or (vloc = top) or (vloc = bottom);
  1799.                     index := 0;
  1800.                     if LR then
  1801.                         index := bor(index, 1);
  1802.                     if LL then
  1803.                         index := bor(index, 2);
  1804.                     if UR then
  1805.                         index := bor(index, 4);
  1806.                     if UL then
  1807.                         index := bor(index, 8);
  1808.                     NewDirection := table[index + 1];
  1809.                     if NewDirection = 'u' then begin
  1810.                             if direction = 'R' then
  1811.                                 NewDirection := 'U'
  1812.                             else
  1813.                                 NewDirection := 'D'
  1814.                         end;
  1815.                     if NewDirection = 'l' then begin
  1816.                             if direction = 'U' then
  1817.                                 NewDirection := 'L'
  1818.                             else
  1819.                                 NewDirection := 'R'
  1820.                         end;
  1821.                     if NewDirection <> direction then begin
  1822.                         LineTo(hloc, vloc);
  1823.                         if SaveCoordinates then begin
  1824.                                 xCoordinates^[count] := hloc;
  1825.                                 yCoordinates^[count] := vloc;
  1826.                                 count := count + 1;
  1827.                             end;
  1828.                     end;
  1829.                     case NewDirection of
  1830.                         'U':  begin
  1831.                                 vloc := vloc - 1;
  1832.                                 LL := UL;
  1833.                                 LR := UR;
  1834.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1835.                                 UR := PixelInside(hloc, vloc - 1);
  1836.                             end;
  1837.                         'D':  begin
  1838.                                 vloc := vloc + 1;
  1839.                                 UL := LL;
  1840.                                 UR := LR;
  1841.                                 LL := PixelInside(hloc - 1, vloc);
  1842.                                 LR := PixelInside(hloc, vloc);
  1843.                             end;
  1844.                         'L':  begin
  1845.                                 hloc := hloc - 1;
  1846.                                 UR := UL;
  1847.                                 LR := LL;
  1848.                                 UL := PixelInside(hloc - 1, vloc - 1);
  1849.                                 LL := PixelInside(hloc - 1, vloc);
  1850.                             end;
  1851.                         'R':  begin
  1852.                                 hloc := hloc + 1;
  1853.                                 UL := UR;
  1854.                                 LL := LR;
  1855.                                 UR := PixelInside(hloc, vloc - 1);
  1856.                                 LR := PixelInside(hloc, vloc);
  1857.                             end;
  1858.                     end;
  1859.                     direction := NewDirection;
  1860.                 until ((hloc = hstart) and (vloc = vstart) and (direction = StartingDirection)) or (count >= MaxCoordinates);
  1861.                 LineTo(hstart, vstart);
  1862.                 if SelectionMode <> NewSelection then
  1863.                     CloseRgn(TempRgn)
  1864.                 else
  1865.                     CloseRgn(roiRgn);
  1866.                 {ShowMessage(StringOf(count, '  ', GetHandleSize(handle(roiRgn)))); beep;}
  1867.                 with roiRgn^^.rgnBBox do
  1868.                     if (count >= MaxCoordinates) or (((right - left) = 0) and ((bottom - top) = 0))  then begin
  1869.                         SetEmptyRgn(roiRgn);
  1870.                         SetPort(SavePort);
  1871.                         TraceEdge := false;
  1872.                         BackgroundIndex := SaveBackground;
  1873.                         nCoordinates := 0;
  1874.                         AbortMacro;
  1875.                         PutError(StringOf('Perimeter too long.', cr, '(', count:1, ' coordinates)'));
  1876.                         exit(TraceEdge);
  1877.                     end;
  1878.                 if (SelectionMode = AddSelection) then begin
  1879.                         if RgnNotTooBig(roiRgn, TempRgn) then
  1880.                             UnionRgn(roiRgn, TempRgn, roiRgn);
  1881.                     end
  1882.                 else if (SelectionMode = SubSelection) then begin
  1883.                         if RgnNotTooBig(roiRgn, TempRgn) then
  1884.                             DiffRgn(roiRgn, TempRgn, roiRgn);
  1885.                     end;
  1886.                 RoiShowing := true;
  1887.                 roiType := TracedRoi;
  1888.                 if SelectionMode = SubSelection then
  1889.                     UpdateScreen(RoiRect);
  1890.                 RoiRect := roiRgn^^.rgnBBox;
  1891.                 BackgroundIndex := SaveBackground;
  1892.             end; {with info}
  1893.         if SelectionMode <> NewSelection then
  1894.             DisposeRgn(TempRgn);
  1895.         SetPort(SavePort);
  1896.         if SaveCoordinates then begin
  1897.                 nCoordinates := count - 1;
  1898.                 MakeCoordinatesRelative;
  1899.             end;
  1900.         TraceEdge := true;
  1901.     end;
  1902.  
  1903.  
  1904.     procedure MarkSelection (count: integer);
  1905.         var
  1906.             SavePort: GrafPtr;
  1907.             NumWidth, NumLeft, NumBottom, SaveForegroundIndex: integer;
  1908.             RoiWidth, inset, hcenter, vcenter: integer;
  1909.             str: str255;
  1910.             r: rect;
  1911.             OutlineWithEllipse: boolean;
  1912.             xc, yc: extended;
  1913.             SaveGDevice: GDHandle;
  1914.     begin
  1915.         OutlineWithEllipse := FitEllipse and OptionKeyWasDown;
  1916.         with info^ do begin
  1917.                 KillRoi;
  1918.                 SetupUndo;
  1919.                 WhatToUndo := UndoOutline;
  1920.                 SaveGDevice := GetGDevice;
  1921.                 SetGDevice(osGDevice);
  1922.                 GetPort(SavePort);
  1923.                 SetPort(GrafPtr(osPort));
  1924.                 SaveForegroundIndex := ForegroundIndex;
  1925.                 SetForegroundColor(WhiteIndex);
  1926.                 PenNormal;
  1927.                 TextFont(Geneva);
  1928.                 TextSize(9);
  1929.                 NumToString(count, str);
  1930.                 with RoiRect do begin
  1931.                         NumWidth := StringWidth(str);
  1932.                         if AnalyzingParticles or OutlineWithEllipse then begin
  1933.                                 xc := xcenter^[count];
  1934.                                 yc := ycenter^[count];
  1935.                                 if SpatiallyCalibrated then begin
  1936.                                         xc := xc * xScale;
  1937.                                         yc := yc * yScale;
  1938.                                     end;
  1939.                                 hcenter := round(xc);
  1940.                                 vcenter := round(yc);
  1941.                                 if InvertYCoordinates then
  1942.                                     vcenter := PicRect.bottom - vcenter - 1
  1943.                             end
  1944.                         else begin
  1945.                                 hcenter := left + (right - left) div 2;
  1946.                                 vcenter := top + (bottom - top) div 2;
  1947.                             end;
  1948.                         NumLeft := hcenter - NumWidth div 2;
  1949.                         NumBottom := vcenter + 3;
  1950.                         if not BinaryPic and not AnalyzingParticles then begin
  1951.                                 FrameRgn(roiRgn);
  1952.                                 if OutlineWithEllipse then
  1953.                                     DrawEllipse;
  1954.                             end;
  1955.                     end;
  1956.                 PenNormal;
  1957.                 SetRect(r, NumLeft - 1, NumBottom - 9, NumLeft + NumWidth + 1, NumBottom + 1);
  1958.                 PaintRoundRect(r, 4, 4);
  1959.                 MoveTo(NumLeft, NumBottom);
  1960.                 TextMode(srcXor);
  1961.                 DrawString(str);
  1962.                 SetForegroundColor(SaveForegroundIndex);
  1963.                 if not analyzingParticles then
  1964.                     UpdateScreen(RoiRect);
  1965.                 SetPort(SavePort);
  1966.                 SetGDevice(SaveGDevice);
  1967.                 changes := true;
  1968.             end;
  1969.     end;
  1970.  
  1971.  
  1972.     function isBinaryImage: boolean;
  1973.         var
  1974.             SaveRoiRect: rect;
  1975.             SaveRedirectFlag: boolean;
  1976.     begin
  1977.         with info^ do begin
  1978.                 SaveRoiRect := RoiRect;
  1979.                 RoiRect := PicRect;
  1980.                 if RedirectSampling then
  1981.                     GetHistogram
  1982.                 else
  1983.                     GetRectHistogram;
  1984.                 BinaryPic := (histogram[0] + histogram[255]) = PixelsPerLine * nLines;
  1985.                 isBinaryImage := BinaryPic;
  1986.                 RoiRect := SaveRoiRect;
  1987.             end;
  1988.     end;
  1989.  
  1990.  
  1991.     function SetupAutoOutline (BinaryPixel: boolean): boolean;
  1992.     begin
  1993.         SetupAutoOutline := false;
  1994.         FindThresholdingMode;
  1995.         if (ThresholdingMode = NoThresholding) or MakingLOI then
  1996.             if isBinaryImage or BinaryPixel then
  1997.                 ThresholdingMode := BinaryImage;
  1998.         if ThresholdingMode = NoThresholding then begin
  1999.                 PutError('Sorry, but you must be thresholding, or working with a binary image, to use the wand tool or to do particle analysis.');
  2000.                 exit(SetupAutoOutline);
  2001.             end;
  2002.         if (ThresholdingMode = GrayMapThresholding) and (GrayMapThreshold = 0) then begin
  2003.                 PutError(' Threshold must be non-zero.');
  2004.                 exit(SetupAutoOutline);
  2005.             end;
  2006.         if not MakingLOI then
  2007.             ShowWatch;
  2008.         SetupAutoOutline := true;
  2009.     end;
  2010.  
  2011.  
  2012.     procedure AutoOutline (start: point);
  2013.         var
  2014.             hloc, vloc: integer;
  2015.             TouchingEdge, BinaryPixel: boolean;
  2016.             direction: char;
  2017.             count: LongInt;
  2018.             Perimeter, CalibratedPerimeter, AspectRatio: extended;
  2019.     begin
  2020.         with start do
  2021.             BinaryPixel := (MyGetPixel(h, v) = WhiteIndex) or (MyGetPixel(h, v) = BlackIndex);
  2022.         if not SetupAutoOutline(BinaryPixel) then
  2023.             exit(AutoOutline);
  2024.         if SelectionMode = NewSelection then
  2025.             KillRoi;
  2026.         with info^ do begin
  2027.                 with start do
  2028.                     if PixelInside(h, v) then begin
  2029.                             repeat
  2030.                                 h := h + 1;
  2031.                             until not PixelInside(h, v) or (h >= PicRect.right);
  2032.                             if not PixelInside(h - 1, v - 1) then
  2033.                                 direction := 'R'
  2034.                             else if PixelInside(h, v - 1) then
  2035.                                 direction := 'L'
  2036.                             else
  2037.                                 direction := 'D';
  2038.                         end
  2039.                     else begin
  2040.                             repeat
  2041.                                 h := h + 1;
  2042.                             until PixelInside(h, v) or (h >= PicRect.right);
  2043.                             direction := 'U';
  2044.                         end;
  2045.                 if start.h >= PicRect.right then begin
  2046.                         beep;
  2047.                         exit(AutoOutline);
  2048.                     end;
  2049.                 if TraceEdge(start.h, start.v, direction, TouchingEdge) then begin
  2050.                     if GetHandleSize(handle(roiRgn)) = 10 then
  2051.                         roiType := RectRoi;
  2052.                     WhatToUndo := NothingToUndo;
  2053.                     if WandAutoMeasure and not MakingLOI then begin
  2054.                             GetHistogram;
  2055.                             ComputeResults;
  2056.                             if WandAdjustAreas then begin
  2057.                                     GetLengthOrPerimeter(Perimeter, CalibratedPerimeter);
  2058.                                     with RoiRect do
  2059.                                         AspectRatio := (right - left) / (bottom - top);
  2060.                                     count := PixelCount^[mCount] + round(Perimeter / 2.0 + AspectRatio * 1.5);
  2061.                                     PixelCount^[mCount] := count;
  2062.                                     if SpatiallyCalibrated then
  2063.                                         mArea^[mCount] := count / (xScale * yScale)
  2064.                                     else
  2065.                                         mArea^[mCount] := count;
  2066.                                 end;
  2067.                             ShowInfo;
  2068.                             AppendResults;
  2069.                             WhatToUndo := UndoMeasurement;
  2070.                             if LabelParticles then
  2071.                                 MarkSelection(mCount);
  2072.                         end;
  2073.                     if not (WandAutoMeasure and LabelParticles) then
  2074.                         RoiShowing := true;
  2075.                     if not MakingLOI then
  2076.                         UpdateScreen(RoiRect);
  2077.                     if not WandAutoMeasure then
  2078.                         measuring := false;
  2079.                 end; {if}
  2080.             end; {with info}
  2081.     end;
  2082.  
  2083.  
  2084.     procedure RedoMeasurement;
  2085.         var
  2086.             SaveN, temp: integer;
  2087.             Canceled: boolean;
  2088.     begin
  2089.         if not isSelectionTool then begin
  2090.                 CurrentTool := SelectionTool;
  2091.                 isSelectionTool := true;
  2092.                 DrawTools;
  2093.             end;
  2094.         temp := GetInt('Measurement to Redo:', mCount, Canceled);
  2095.         if canceled then
  2096.             exit(RedoMeasurement);
  2097.         MeasurementToRedo := temp;
  2098.         if (MeasurementToRedo >= 1) and (MeasurementToRedo <= mCount) then begin
  2099.                 SaveN := mCount;
  2100.                 mCount := MeasurementToRedo;
  2101.                 ShowInfo;
  2102.                 mCount := SaveN;
  2103.             end
  2104.         else begin
  2105.                 beep;
  2106.                 MeasurementToRedo := 0;
  2107.             end;
  2108.     end;
  2109.  
  2110.  
  2111.     procedure DeleteMeasurement;
  2112.         var
  2113.             nToDelete, i: integer;
  2114.             Canceled: boolean;
  2115.     begin
  2116.         nToDelete := GetInt('Measurement to delete:', mCount, Canceled);
  2117.         if (nToDelete >= 1) and (nToDelete <= mCount) and not Canceled then begin
  2118.                 for i := nToDelete to mCount - 1 do begin
  2119.                         mean^[i] := mean^[i + 1];
  2120.                         sd^[i] := sd^[i + 1];
  2121.                         PixelCount^[i] := PixelCount^[i + 1];
  2122.                         mArea^[i] := mArea^[i + 1];
  2123.                         mode^[i] := mode^[i + 1];
  2124.                         IntegratedDensity^[i] := IntegratedDensity^[i + 1];
  2125.                         idBackground^[i] := idBackground^[i + 1];
  2126.                         xcenter^[i] := xcenter^[i + 1];
  2127.                         ycenter^[i] := ycenter^[i + 1];
  2128.                         MajorAxis^[i] := MajorAxis^[i + 1];
  2129.                         MinorAxis^[i] := MinorAxis^[i + 1];
  2130.                         orientation^[i] := orientation^[i + 1];
  2131.                         mMin^[i] := mMin^[i + 1];
  2132.                         mMax^[i] := mMax^[i + 1];
  2133.                         plength^[i] := plength^[i + 1];
  2134.                     end; {for}
  2135.                 mCount := mCount - 1;
  2136.                 if mCount = 0 then begin
  2137.                         UnsavedResults := false;
  2138.                         beep;
  2139.                     end;
  2140.                 UpdateList;
  2141.             end
  2142.         else if not Canceled then
  2143.             beep;
  2144.     end;
  2145.  
  2146.  
  2147.     function DoAPDialog: boolean;
  2148.         const
  2149.             MinID = 6;
  2150.             MaxID = 7;
  2151.             LabelID = 8;
  2152.             OutlineID = 9;
  2153.             IgnoreID = 10;
  2154.             IncludeHolesID = 11;
  2155.             ResetID = 12;
  2156.         var
  2157.             mylog: DialogPtr;
  2158.             item: integer;
  2159.     begin
  2160.         InitCursor;
  2161.         mylog := GetNewDialog(220, nil, pointer(-1));
  2162.         SetDNum(MyLog, MinID, MinParticleSize);
  2163.         SetDNum(MyLog, MaxID, MaxParticleSize);
  2164.         SetDlogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2165.         SetDlogItem(mylog, LabelID, ord(LabelParticles));
  2166.         SetDlogItem(mylog, OutlineID, ord(OutlineParticles));
  2167.         SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2168.         SetDlogItem(mylog, ResetID, ord(APReset));
  2169.         repeat
  2170.             ModalDialog(nil, item);
  2171.             if item = MinID then
  2172.                 MinParticleSize := GetDNum(MyLog, MinID);
  2173.             if item = MaxID then
  2174.                 MaxParticleSize := GetDNum(MyLog, MaxID);
  2175.             if item = IgnoreID then begin
  2176.                     IgnoreParticlesTouchingEdge := not IgnoreParticlesTouchingEdge;
  2177.                     SetDlogItem(mylog, IgnoreID, ord(IgnoreParticlesTouchingEdge));
  2178.                 end;
  2179.             if item = LabelID then begin
  2180.                     LabelParticles := not LabelParticles;
  2181.                     SetDlogItem(mylog, LabelID, ord(LabelParticles));
  2182.                 end;
  2183.             if item = OutlineID then begin
  2184.                     OutlineParticles := not OutlineParticles;
  2185.                     SetDlogItem(mylog, OutlineID, ord(OutlineParticles));
  2186.                 end;
  2187.             if item = IncludeHolesID then begin
  2188.                     IncludeHoles := not IncludeHoles;
  2189.                     SetDlogItem(mylog, IncludeHolesID, ord(IncludeHoles));
  2190.                 end;
  2191.             if item = ResetID then begin
  2192.                     APReset := not APReset;
  2193.                     SetDlogItem(mylog, ResetID, ord(APReset));
  2194.                 end;
  2195.         until (item = ok) or (item = cancel);
  2196.         DisposeDialog(mylog);
  2197.         if MinParticleSize < 1 then
  2198.             MinParticleSize := 1;
  2199.         if MaxParticleSize > 9999999 then
  2200.             MaxParticleSize := 9999999;
  2201.         if MaxParticleSize <= MinParticleSize then begin
  2202.                 MinParticleSize := 1;
  2203.                 MaxParticleSize := 999999;
  2204.             end;
  2205.         DoAPDialog := item <> cancel;
  2206.     end;
  2207.  
  2208.  
  2209.     procedure AnalyzeParticles;
  2210.     {
  2211.     Here's how it works:      (thanks to Stein Roervik)
  2212.         for each line do
  2213.               for each pixel in this line do
  2214.                 if the pixel value is "inside" the threshold range then
  2215.                   trace the edge to mark the object
  2216.                   do the measurement
  2217.                   fill the object with a colour that is outside the threshold range
  2218.                 else
  2219.                   continue the scan
  2220.     }
  2221.         var
  2222.             hloc, vloc, AlertID, index, MaxTriesPerLine, nParticles: integer;
  2223.             SaveSliceState, TouchingEdge, DrawOutlines, AutoSelectAll, finished, OutsideSelection: boolean;
  2224.             SaveForegroundIndex, SaveBackgroundIndex, EraseIndex, OutlineIndex: integer;
  2225.             tPort: GrafPtr;
  2226.             ScanRect: rect;
  2227.             side: (TopSide, RightSide, BottomSide, LeftSide);
  2228.             dstRgn: rgnHandle;
  2229.             StartCount: integer;
  2230.  
  2231.         function PixelInside: boolean;
  2232.             var
  2233.                 value: integer;
  2234.                 offset: LongInt;
  2235.                 p: ptr;
  2236.         begin
  2237.             with info^ do begin {MyGetPixel inlined to speed things up.}
  2238.                     offset := vloc * BytesPerRow + hloc;
  2239.                     p := ptr(ord4(PicBaseAddr) + offset);
  2240.                 end;
  2241.             value := BAND(p^, 255);
  2242.             case ThresholdingMode of
  2243.                 DensitySlice: 
  2244.                     PixelInside := (value >= SliceStart) and (value <= SliceEnd);
  2245.                 GrayMapThresholding: 
  2246.                     PixelInside := value >= GrayMapThreshold;
  2247.                 BinaryImage: 
  2248.                     PixelInside := value = BlackIndex;
  2249.             end;
  2250.         end;
  2251.  
  2252.         procedure LabelBlobs;
  2253.             var
  2254.                 i,j: integer;
  2255.         begin
  2256.             j := 0;
  2257.             if (StartCount - 1 + nParticles) <= MaxMeasurements then
  2258.                 for i := StartCount to mCount do begin
  2259.                         MarkSelection(i);
  2260.                         j := j + 1;
  2261.                         if j mod 50 = 0 then
  2262.                             UpdatePicWindow;
  2263.                         if CommandPeriod then begin
  2264.                                 beep;
  2265.                                 leave;
  2266.                             end;
  2267.                     end;
  2268.         end;
  2269.         
  2270.  
  2271.         procedure abort;
  2272.         begin
  2273.             SetPort(tPort);
  2274.             if LabelParticles then
  2275.                 LabelBlobs;
  2276.             DensitySlicing := SaveSliceState;
  2277.             SetForegroundColor(SaveForegroundIndex);
  2278.             SetBackgroundColor(SaveBackgroundIndex);
  2279.             KillRoi;
  2280.             UpdatePicWindow;
  2281.             WhatToUndo := UndoEdit;
  2282.             UndoFromClip := true;
  2283.             AnalyzingParticles := false;
  2284.             DisposeRgn(dstRgn);
  2285.         end;
  2286.  
  2287.  
  2288.     begin
  2289.         with info^ do begin
  2290.                 if NotInBounds or NoUndo then
  2291.                     exit(AnalyzeParticles);
  2292.                 if not SetupAutoOutline(false) then
  2293.                     exit(AnalyzeParticles);
  2294.                 if not macro and not OptionKeyWasDown then
  2295.                     if not DoAPDialog then
  2296.                         exit(AnalyzeParticles);
  2297.                 AutoSelectAll := not RoiShowing;
  2298.                 if AutoSelectAll then
  2299.                     SelectAll(false);
  2300.                 ScanRect := RoiRect;
  2301.                 if not AutoSelectAll then
  2302.                     with ScanRect do begin
  2303.                             left := picrect.left;
  2304.                             right := PicRect.right;
  2305.                         end;
  2306.                 KillRoi;
  2307.                 if APReset then begin
  2308.                         ResetCounter;
  2309.                         if mCount > 0 then
  2310.                             exit(AnalyzeParticles);
  2311.                     end;
  2312.                 StartCount := mCount + 1;
  2313.                 UpdatePicWindow;
  2314.                 SetupUndoFromClip;
  2315.                 SaveSliceState := DensitySlicing;
  2316.                 SaveForegroundIndex := ForegroundIndex;
  2317.                 SaveBackgroundIndex := BackgroundIndex;
  2318.                 SetForegroundColor(WhiteIndex);
  2319.                 DensitySlicing := false;
  2320.                 DrawOutlines := false;
  2321.                 case ThresholdingMode of
  2322.                     DensitySlice:  begin
  2323.                             EraseIndex := SliceStart - 1;
  2324.                             if EraseIndex < 0 then
  2325.                                 EraseIndex := WhiteIndex;
  2326.                             DrawOutlines := OutlineParticles;
  2327.                             OutLineIndex := BlackIndex;
  2328.                         end;
  2329.                     GrayMapThresholding:  begin
  2330.                             EraseIndex := GrayMapThreshold - 1;
  2331.                             if EraseIndex < 0 then
  2332.                                 EraseIndex := WhiteIndex;
  2333.                         end;
  2334.                     BinaryImage:  begin
  2335.                             DrawOutlines := OutlineParticles;
  2336.                             OutLineIndex := 254;
  2337.                             EraseIndex := 128;
  2338.                         end;
  2339.                 end;
  2340.                 AnalyzingParticles := true;
  2341.                 nParticles := 0;
  2342.                 GetPort(tPort);
  2343.                 SetPort(GrafPtr(osPort));
  2344.                 dstRgn := NewRgn;
  2345.                 SelectionMode := NewSelection;
  2346.                 ShowWatch;
  2347.                 with ScanRect do
  2348.                     for vloc := top to bottom - 1 do
  2349.                         for hloc := left to right - 1 do begin
  2350.                                 if PixelInside then begin
  2351.                                         if TraceEdge(hloc, vloc, 'U', TouchingEdge) then begin
  2352.                                                 nParticles := nParticles + 1;
  2353.                                                 RoiShowing := false;
  2354.                                                 if mCount < MaxMeasurements then begin
  2355.                                                         GetHistogram;
  2356.                                                         ComputeResults;
  2357.                                                     end;
  2358.                                                 SetBackgroundColor(EraseIndex);
  2359.                                                 EraseRgn(roiRgn);
  2360.                                                 if AutoSelectAll then
  2361.                                                     OutSideSelection := false
  2362.                                                 else begin
  2363.                                                         SectRgn(roiRgn, NoInfo^.RoiRgn, dstRgn);
  2364.                                                         OutSideSelection := EmptyRgn(dstRgn);
  2365.                                                     end;
  2366.                                                 if (PixelCount^[mCount] < MinParticleSize) or (PixelCount^[mCount] > MaxParticleSize) or TouchingEdge or OutsideSelection then begin
  2367.                                                         mCount := mCount - 1;
  2368.                                                         nParticles := nParticles - 1;
  2369.                                                         UpdateScreen(RoiRect);
  2370.                                                     end
  2371.                                                 else begin
  2372.                                                         if DrawOutlines then begin
  2373.                                                                 SetForegroundColor(OutlineIndex);
  2374.                                                                 FrameRgn(roiRgn);
  2375.                                                             end;
  2376.                                                         UpdateScreen(RoiRect);
  2377.                                                         if nParticles <= MaxMeasurements then
  2378.                                                             AppendResults;
  2379.                                                         if (nParticles mod 10) = 0 then ShowMessage(long2str(nParticles));
  2380.                                                         if nParticles = MaxMeasurements then
  2381.                                                             beep;
  2382.                                                         if CommandPeriod or (AnalyzingParticles = false) then begin {quit}
  2383.                                                                 beep;
  2384.                                                                 abort;
  2385.                                                                 exit(AnalyzeParticles);
  2386.                                                             end; {quit}
  2387.                                                     end;
  2388.                                             end   {if TraceEdge}
  2389.                                             else begin
  2390.                                                 abort; {perimeter too large}
  2391.                                                 exit(AnalyzeParticles);
  2392.                                             end;
  2393.                                     end; {if PixelInside}
  2394.                             end; {for}
  2395.             end; {with}
  2396.         ShowMessage(StringOf('Count=',nParticles:1));
  2397.         SetPort(tPort);
  2398.         if LabelParticles then
  2399.             LabelBlobs;
  2400.         DensitySlicing := SaveSliceState;
  2401.         SetForegroundColor(SaveForegroundIndex);
  2402.         SetBackgroundColor(SaveBackgroundIndex);
  2403.         KillRoi;
  2404.         UpdatePicWindow;
  2405.         if ThresholdingMode = GrayMapThresholding then
  2406.             ResetGrayMap;
  2407.         WhatToUndo := UndoEdit;
  2408.         UndoFromClip := true;
  2409.         AnalyzingParticles := false;
  2410.         DisposeRgn(dstRgn);
  2411.     end;
  2412.  
  2413.  
  2414.     procedure MakeNonStraightLineRoi (RoiKind: RoiTypeType);
  2415.         var
  2416.             i, ff: integer;
  2417.             SaveInfo: InfoPtr;
  2418.             pt, spt, start: point;
  2419.             SaveGDevice: GDHandle;
  2420.     begin
  2421.         SetupUndoInfoRec;
  2422.         SaveInfo := Info;
  2423.         Info := UndoInfo;
  2424.         SaveGDevice := GetGDevice;
  2425.         SetGDevice(osGDevice);
  2426.         with info^ do begin
  2427.                 magnification := SaveInfo^.magnification;
  2428.                 SrcRect := SaveInfo^.SrcRect;
  2429.                 BinaryPic := true;
  2430.                 SetPort(GrafPtr(osPort));
  2431.             end;
  2432.         pmForeColor(BlackIndex);
  2433.         pmBackColor(WhiteIndex);
  2434.         PenNormal;
  2435.         PenSize(LineWidth, LineWidth);
  2436.         EraseRect(info^.PicRect);
  2437.         ff := LineWidth div 2;
  2438.         if ff < 0 then
  2439.             ff := 0;
  2440.         MakingLOI := true;
  2441.         ConvertCoordinates;
  2442.         spt.h := xCoordinates^[1];
  2443.         spt.v := yCoordinates^[1];
  2444.         MoveTo(spt.h - ff, spt.v - ff);
  2445.         for i := 2 to nCoordinates do begin
  2446.                 pt.h := xCoordinates^[i];
  2447.                 pt.v := yCoordinates^[i];
  2448.                 LineTo(pt.h - ff, pt.v - ff);
  2449.             end;
  2450.         start := spt;
  2451.         start.h := start.h - 1;
  2452.         AutoOutline(start);
  2453.         MakingLOI := false;
  2454.         info^.RoiShowing := false;
  2455.         Info := SaveInfo;
  2456.         SetGDevice(SaveGDevice);
  2457.         with info^ do begin
  2458.                 CopyRgn(UndoInfo^.roiRgn, roiRgn);
  2459.                 RoiRect := UndoInfo^.RoiRect;
  2460.                 SetEmptyRgn(UndoInfo^.roiRgn);
  2461.                 RoiShowing := true;
  2462.                 SetupUndo;
  2463.                 roiType := RoiKind;
  2464.                 with RoiRect do begin
  2465.                         LX1 := spt.h - left;
  2466.                         LY1 := spt.v - top;
  2467.                         LX2 := pt.h - left;
  2468.                         LY2 := pt.v - top;
  2469.                     end;
  2470.             end; {with info^}
  2471.         MakeCoordinatesRelative;
  2472.     end;
  2473.  
  2474.  
  2475. end.